chromium/chromeos/ash/components/network/hotspot_util.cc

// Copyright 2022 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/components/network/hotspot_util.h"

#include <optional>

#include "base/strings/string_number_conversions.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/services/hotspot_config/public/mojom/cros_hotspot_config.mojom.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace ash {

namespace {

hotspot_config::mojom::WiFiBand ShillBandToMojom(
    const std::string& shill_band) {
  using hotspot_config::mojom::WiFiBand;

  if (shill_band == shill::kBand2GHz) {
    return WiFiBand::k2_4GHz;
  }
  if (shill_band == shill::kBandAll) {
    return WiFiBand::kAutoChoose;
  }
  NET_LOG(ERROR) << "Unexpected shill tethering band: " << shill_band;
  return WiFiBand::kAutoChoose;
}

std::string MojomBandToString(hotspot_config::mojom::WiFiBand mojom_band) {
  using hotspot_config::mojom::WiFiBand;

  switch (mojom_band) {
    case WiFiBand::k2_4GHz:
      return shill::kBand2GHz;
    case WiFiBand::kAutoChoose:
      return shill::kBandAll;
  }
}

std::string MojomSecurityToString(
    hotspot_config::mojom::WiFiSecurityMode mojom_security) {
  using hotspot_config::mojom::WiFiSecurityMode;

  switch (mojom_security) {
    case WiFiSecurityMode::kWpa2:
      return shill::kSecurityWpa2;
    case WiFiSecurityMode::kWpa3:
      return shill::kSecurityWpa3;
    case WiFiSecurityMode::kWpa2Wpa3:
      return shill::kSecurityWpa2Wpa3;
  }
}

std::string HexDecode(const std::string& hex_ssid) {
  std::string ssid;
  if (!base::HexStringToString(hex_ssid, &ssid)) {
    NET_LOG(ERROR) << "Error decoding HexSSID: " << hex_ssid;
  }

  return ssid;
}

}  // namespace

hotspot_config::mojom::HotspotState ShillTetheringStateToMojomState(
    const std::string& shill_state) {
  using hotspot_config::mojom::HotspotState;
  if (shill_state == shill::kTetheringStateActive) {
    return HotspotState::kEnabled;
  }

  if (shill_state == shill::kTetheringStateIdle) {
    return HotspotState::kDisabled;
  }

  if (shill_state == shill::kTetheringStateStarting ||
      shill_state == shill::kTetheringStateRestarting) {
    return HotspotState::kEnabling;
  }

  if (shill_state == shill::kTetheringStateStopping) {
    return HotspotState::kDisabling;
  }

  NET_LOG(ERROR) << "Unexpected shill tethering state: " << shill_state;
  return HotspotState::kDisabled;
}

hotspot_config::mojom::DisableReason ShillTetheringIdleReasonToMojomState(
    const std::string& idle_reason) {
  using hotspot_config::mojom::DisableReason;

  if (idle_reason == shill::kTetheringIdleReasonInactive) {
    return DisableReason::kAutoDisabled;
  }

  if (idle_reason == shill::kTetheringIdleReasonUpstreamDisconnect) {
    return DisableReason::kUpstreamNetworkNotAvailable;
  }

  if (idle_reason == shill::kTetheringIdleReasonError) {
    return DisableReason::kInternalError;
  }

  if (idle_reason == shill::kTetheringIdleReasonSuspend) {
    return DisableReason::kSuspended;
  }

  if (idle_reason == shill::kTetheringIdleReasonUserExit ||
      idle_reason == shill::kTetheringIdleReasonClientStop) {
    return DisableReason::kUserInitiated;
  }

  if (idle_reason == shill::kTetheringIdleReasonConfigChange) {
    return DisableReason::kRestart;
  }

  if (idle_reason == shill::kTetheringIdleReasonUpstreamNoInternet) {
    return DisableReason::kUpstreamNoInternet;
  }

  if (idle_reason == shill::kTetheringIdleReasonDownstreamLinkDisconnect) {
    return DisableReason::kDownstreamLinkDisconnect;
  }

  if (idle_reason == shill::kTetheringIdleReasonDownstreamNetworkDisconnect) {
    return DisableReason::kDownstreamNetworkDisconnect;
  }

  if (idle_reason == shill::kTetheringIdleReasonStartTimeout) {
    return DisableReason::kStartTimeout;
  }

  if (idle_reason == shill::kTetheringIdleReasonUpstreamNotAvailable) {
    return DisableReason::kUpstreamNotAvailable;
  }

  NET_LOG(ERROR) << "Unexpected idle reason: " << idle_reason;
  return DisableReason::kUnknownError;
}

hotspot_config::mojom::WiFiSecurityMode ShillSecurityToMojom(
    const std::string& shill_security) {
  using hotspot_config::mojom::WiFiSecurityMode;

  if (shill_security == shill::kSecurityWpa2) {
    return WiFiSecurityMode::kWpa2;
  }
  if (shill_security == shill::kSecurityWpa3) {
    return WiFiSecurityMode::kWpa3;
  }
  if (shill_security == shill::kSecurityWpa2Wpa3) {
    return WiFiSecurityMode::kWpa2Wpa3;
  }

  NET_LOG(ERROR) << "Unexpeted shill tethering security mode: "
                 << shill_security;
  return WiFiSecurityMode::kWpa2;
}

hotspot_config::mojom::HotspotConfigPtr ShillTetheringConfigToMojomConfig(
    const base::Value::Dict& shill_tethering_config) {
  using hotspot_config::mojom::HotspotConfig;

  auto result = HotspotConfig::New();
  std::optional<bool> auto_disable =
      shill_tethering_config.FindBool(shill::kTetheringConfAutoDisableProperty);
  if (!auto_disable) {
    NET_LOG(ERROR) << "Auto_disable not found in tethering config.";
  }
  result->auto_disable = auto_disable.value_or(true);
  const std::string* wifi_band =
      shill_tethering_config.FindString(shill::kTetheringConfBandProperty);
  if (!wifi_band) {
    NET_LOG(ERROR) << "WiFi band not found in tethering config.";
    result->band = hotspot_config::mojom::WiFiBand::kAutoChoose;
  } else {
    result->band = ShillBandToMojom(*wifi_band);
  }

  const std::string* security =
      shill_tethering_config.FindString(shill::kTetheringConfSecurityProperty);
  if (!security) {
    NET_LOG(ERROR) << "WiFi security mode not found in tethering config.";
    result->security = hotspot_config::mojom::WiFiSecurityMode::kWpa2;
  } else {
    result->security = ShillSecurityToMojom(*security);
  }

  const std::string* ssid =
      shill_tethering_config.FindString(shill::kTetheringConfSSIDProperty);
  if (!ssid) {
    NET_LOG(ERROR) << "SSID not found in tethering config.";
  }
  result->ssid = ssid ? HexDecode(*ssid) : std::string();
  const std::string* passphrase = shill_tethering_config.FindString(
      shill::kTetheringConfPassphraseProperty);
  if (!passphrase) {
    NET_LOG(ERROR) << "Passphrase not found in tethering config.";
  }
  result->passphrase = passphrase ? *passphrase : std::string();
  std::optional<bool> bssid_randomization =
      shill_tethering_config.FindBool(shill::kTetheringConfMARProperty);
  if (!bssid_randomization) {
    NET_LOG(ERROR) << shill::kTetheringConfMARProperty
                   << " not found in tethering config.";
  }
  // Default to true for privacy concern, specifically, to lower the possibility
  // of a user tracking.
  result->bssid_randomization = bssid_randomization.value_or(true);
  return result;
}

base::Value::Dict MojomConfigToShillConfig(
    const hotspot_config::mojom::HotspotConfigPtr mojom_config) {
  using hotspot_config::mojom::HotspotConfig;

  base::Value::Dict result;
  result.Set(shill::kTetheringConfAutoDisableProperty,
             mojom_config->auto_disable);
  result.Set(shill::kTetheringConfBandProperty,
             MojomBandToString(mojom_config->band));
  result.Set(shill::kTetheringConfSecurityProperty,
             MojomSecurityToString(mojom_config->security));
  result.Set(shill::kTetheringConfSSIDProperty,
             base::HexEncode(mojom_config->ssid));
  result.Set(shill::kTetheringConfPassphraseProperty, mojom_config->passphrase);
  result.Set(shill::kTetheringConfMARProperty,
             mojom_config->bssid_randomization);
  return result;
}

hotspot_config::mojom::HotspotControlResult SetTetheringEnabledResultToMojom(
    const std::string& shill_enabled_result) {
  using hotspot_config::mojom::HotspotControlResult;

  if (shill_enabled_result == shill::kTetheringEnableResultSuccess) {
    return HotspotControlResult::kSuccess;
  }
  if (shill_enabled_result == shill::kTetheringEnableResultInvalidProperties) {
    return HotspotControlResult::kInvalidConfiguration;
  }
  if (shill_enabled_result ==
      shill::kTetheringEnableResultUpstreamNotAvailable) {
    return HotspotControlResult::kUpstreamNotAvailable;
  }
  if (shill_enabled_result ==
      shill::kTetheringEnableResultNetworkSetupFailure) {
    return HotspotControlResult::kNetworkSetupFailure;
  }
  if (shill_enabled_result ==
      shill::kTetheringEnableResultDownstreamWiFiFailure) {
    return HotspotControlResult::kDownstreamWifiFailure;
  }
  if (shill_enabled_result == shill::kTetheringEnableResultUpstreamFailure) {
    return HotspotControlResult::kUpstreamFailure;
  }
  if (shill_enabled_result == shill::kTetheringEnableResultWrongState) {
    return HotspotControlResult::kAlreadyFulfilled;
  }

  NET_LOG(ERROR) << "Unknown enable/disable tethering error: "
                 << shill_enabled_result;
  return HotspotControlResult::kUnknownFailure;
}

}  // namespace ash