chromium/chromeos/ash/components/tether/wifi_hotspot_disconnector_impl.cc

// Copyright 2017 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/tether/wifi_hotspot_disconnector_impl.h"

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/components/network/network_connection_handler.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/tether/network_configuration_remover.h"
#include "chromeos/ash/components/tether/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"

namespace ash {

namespace tether {

// static
void WifiHotspotDisconnectorImpl::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterStringPref(prefs::kDisconnectingWifiNetworkPath,
                               std::string());
}

WifiHotspotDisconnectorImpl::WifiHotspotDisconnectorImpl(
    NetworkConnectionHandler* network_connection_handler,
    NetworkStateHandler* network_state_handler,
    PrefService* pref_service,
    NetworkConfigurationRemover* network_configuration_remover)
    : network_connection_handler_(network_connection_handler),
      network_state_handler_(network_state_handler),
      pref_service_(pref_service),
      network_configuration_remover_(network_configuration_remover) {
  std::string disconnecting_wifi_path_from_previous_session =
      pref_service_->GetString(prefs::kDisconnectingWifiNetworkPath);
  if (disconnecting_wifi_path_from_previous_session.empty())
    return;

  // If a previous disconnection attempt was aborted before it could be fully
  // completed, clean up the leftover network configuration.
  network_configuration_remover_->RemoveNetworkConfigurationByPath(
      disconnecting_wifi_path_from_previous_session);
  pref_service_->ClearPref(prefs::kDisconnectingWifiNetworkPath);
}

WifiHotspotDisconnectorImpl::~WifiHotspotDisconnectorImpl() = default;

void WifiHotspotDisconnectorImpl::DisconnectFromWifiHotspot(
    const std::string& wifi_network_guid,
    base::OnceClosure success_callback,
    StringErrorCallback error_callback) {
  const NetworkState* wifi_network_state =
      network_state_handler_->GetNetworkStateFromGuid(wifi_network_guid);
  if (!wifi_network_state) {
    PA_LOG(ERROR) << "Wi-Fi NetworkState for GUID " << wifi_network_guid << " "
                  << "does not exist. Cannot disconnect.";
    std::move(error_callback).Run(NetworkConnectionHandler::kErrorNotFound);
    return;
  }

  if (!wifi_network_state->IsConnectedState()) {
    PA_LOG(ERROR) << "Wi-Fi NetworkState for GUID " << wifi_network_guid << " "
                  << "is not connected. Cannot disconnect.";
    std::move(error_callback).Run(NetworkConnectionHandler::kErrorNotConnected);
    return;
  }

  const std::string wifi_network_path = wifi_network_state->path();
  DCHECK(!wifi_network_path.empty());

  // Before starting disconnection, log the disconnecting Wi-Fi GUID to prefs.
  // Under normal circumstances, the GUID will be cleared as part of
  // CleanUpAfterWifiDisconnection(). However, when the user logs out,
  // this WifiHotspotDisconnectorImpl instance will be deleted before one of the
  // callbacks passed below to DisconnectNetwork() can be called, and the
  // GUID will remain in prefs until the next time the user logs in, at which
  // time the associated network configuration can be removed.
  pref_service_->Set(prefs::kDisconnectingWifiNetworkPath,
                     base::Value(wifi_network_path));

  auto split_callback = base::SplitOnceCallback(std::move(error_callback));
  network_connection_handler_->DisconnectNetwork(
      wifi_network_path,
      base::BindOnce(&WifiHotspotDisconnectorImpl::OnSuccessfulWifiDisconnect,
                     weak_ptr_factory_.GetWeakPtr(), wifi_network_guid,
                     wifi_network_path, std::move(success_callback),
                     std::move(split_callback.first)),
      base::BindOnce(&WifiHotspotDisconnectorImpl::OnFailedWifiDisconnect,
                     weak_ptr_factory_.GetWeakPtr(), wifi_network_guid,
                     wifi_network_path, std::move(split_callback.second)));
}

void WifiHotspotDisconnectorImpl::OnSuccessfulWifiDisconnect(
    const std::string& wifi_network_guid,
    const std::string& wifi_network_path,
    base::OnceClosure success_callback,
    StringErrorCallback error_callback) {
  PA_LOG(VERBOSE) << "Successfully disconnected from Wi-Fi network with GUID "
                  << wifi_network_guid << ".";
  CleanUpAfterWifiDisconnection(wifi_network_path, std::move(success_callback),
                                std::move(error_callback));
}

void WifiHotspotDisconnectorImpl::OnFailedWifiDisconnect(
    const std::string& wifi_network_guid,
    const std::string& wifi_network_path,
    StringErrorCallback error_callback,
    const std::string& error_name) {
  PA_LOG(ERROR) << "Failed to disconnect from Wi-Fi network with GUID "
                << wifi_network_guid << ". Error name: " << error_name;
  CleanUpAfterWifiDisconnection(wifi_network_path, base::OnceClosure(),
                                std::move(error_callback));
}

void WifiHotspotDisconnectorImpl::CleanUpAfterWifiDisconnection(
    const std::string& wifi_network_path,
    base::OnceClosure success_callback,
    StringErrorCallback error_callback) {
  network_configuration_remover_->RemoveNetworkConfigurationByPath(
      wifi_network_path);
  pref_service_->ClearPref(prefs::kDisconnectingWifiNetworkPath);

  if (success_callback)
    std::move(success_callback).Run();
  else
    std::move(error_callback)
        .Run(NetworkConnectionHandler::kErrorDisconnectFailed);
}

}  // namespace tether

}  // namespace ash