// 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/active_host.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/components/multidevice/remote_device_ref.h"
#include "chromeos/ash/components/tether/pref_names.h"
#include "chromeos/ash/components/tether/tether_host_fetcher.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace ash::tether {
// static
std::string ActiveHost::StatusToString(const ActiveHostStatus& status) {
switch (status) {
case ActiveHostStatus::DISCONNECTED:
return "DISCONNECTED";
case ActiveHostStatus::CONNECTING:
return "CONNECTING";
case ActiveHostStatus::CONNECTED:
return "CONNECTED";
}
}
bool operator==(const ActiveHost::ActiveHostChangeInfo& first,
const ActiveHost::ActiveHostChangeInfo& second) {
bool new_devices_equal;
if (first.new_active_host) {
new_devices_equal = second.new_active_host &&
*first.new_active_host == *second.new_active_host;
} else {
new_devices_equal = !second.new_active_host;
}
return first.new_status == second.new_status &&
first.old_status == second.old_status && new_devices_equal &&
first.old_active_host_id == second.old_active_host_id &&
first.new_tether_network_guid == second.new_tether_network_guid &&
first.old_tether_network_guid == second.old_tether_network_guid &&
first.new_wifi_network_guid == second.new_wifi_network_guid &&
first.old_wifi_network_guid == second.old_wifi_network_guid;
}
ActiveHost::ActiveHostChangeInfo::ActiveHostChangeInfo()
: new_status(ActiveHostStatus::DISCONNECTED),
old_status(ActiveHostStatus::DISCONNECTED) {}
ActiveHost::ActiveHostChangeInfo::ActiveHostChangeInfo(
ActiveHostStatus new_status,
ActiveHostStatus old_status,
std::optional<multidevice::RemoteDeviceRef> new_active_host,
std::string old_active_host_id,
std::string new_tether_network_guid,
std::string old_tether_network_guid,
std::string new_wifi_network_guid,
std::string old_wifi_network_guid)
: new_status(new_status),
old_status(old_status),
new_active_host(new_active_host),
old_active_host_id(old_active_host_id),
new_tether_network_guid(new_tether_network_guid),
old_tether_network_guid(old_tether_network_guid),
new_wifi_network_guid(new_wifi_network_guid),
old_wifi_network_guid(old_wifi_network_guid) {}
ActiveHost::ActiveHostChangeInfo::ActiveHostChangeInfo(
const ActiveHostChangeInfo& other) = default;
ActiveHost::ActiveHostChangeInfo::~ActiveHostChangeInfo() = default;
ActiveHost::ActiveHost(TetherHostFetcher* tether_host_fetcher,
PrefService* pref_service)
: tether_host_fetcher_(tether_host_fetcher), pref_service_(pref_service) {}
ActiveHost::~ActiveHost() = default;
// static
void ActiveHost::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(
prefs::kActiveHostStatus,
static_cast<int>(ActiveHostStatus::DISCONNECTED));
registry->RegisterStringPref(prefs::kActiveHostDeviceId, "");
registry->RegisterStringPref(prefs::kTetherNetworkGuid, "");
registry->RegisterStringPref(prefs::kWifiNetworkGuid, "");
}
void ActiveHost::SetActiveHostDisconnected() {
SetActiveHost(ActiveHostStatus::DISCONNECTED, "" /* active_host_device_id */,
"" /* tether_network_guid */, "" /* wifi_network_guid */);
}
void ActiveHost::SetActiveHostConnecting(
const std::string& active_host_device_id,
const std::string& tether_network_guid) {
DCHECK(!active_host_device_id.empty());
SetActiveHost(ActiveHostStatus::CONNECTING, active_host_device_id,
tether_network_guid, "" /* wifi_network_guid */);
}
void ActiveHost::SetActiveHostConnected(
const std::string& active_host_device_id,
const std::string& tether_network_guid,
const std::string& wifi_network_guid) {
DCHECK(!active_host_device_id.empty());
DCHECK(!tether_network_guid.empty());
DCHECK(!wifi_network_guid.empty());
SetActiveHost(ActiveHostStatus::CONNECTED, active_host_device_id,
tether_network_guid, wifi_network_guid);
}
void ActiveHost::GetActiveHost(ActiveHostCallback active_host_callback) {
ActiveHostStatus status = GetActiveHostStatus();
if (status == ActiveHostStatus::DISCONNECTED) {
std::move(active_host_callback)
.Run(status, std::nullopt /* active_host */,
"" /* tether_network_guid */, "" /* wifi_network_guid */);
return;
}
std::string active_host_device_id = GetActiveHostDeviceId();
DCHECK(!active_host_device_id.empty());
std::optional<multidevice::RemoteDeviceRef> active_host =
tether_host_fetcher_->GetTetherHost();
if (GetActiveHostDeviceId().empty() || !active_host) {
DCHECK(GetActiveHostStatus() == ActiveHostStatus::DISCONNECTED);
DCHECK(GetTetherNetworkGuid().empty());
DCHECK(GetWifiNetworkGuid().empty());
// If the active host became disconnected while the tether host was being
// fetched, forward this information to the callback.
std::move(active_host_callback)
.Run(ActiveHostStatus::DISCONNECTED, std::nullopt /* active_host */,
"" /* wifi_network_guid */, "" /* tether_network_guid */);
return;
}
if (GetActiveHostDeviceId() != active_host->GetDeviceId()) {
// If the active host has changed while the tether host was being fetched,
// perform the fetch again.
GetActiveHost(std::move(active_host_callback));
return;
}
if (GetActiveHostStatus() == ActiveHostStatus::CONNECTING) {
DCHECK(!GetTetherNetworkGuid().empty());
DCHECK(GetWifiNetworkGuid().empty());
std::move(active_host_callback)
.Run(ActiveHostStatus::CONNECTING, active_host,
GetTetherNetworkGuid() /* tether_network_guid */,
"" /* wifi_network_guid */);
return;
}
DCHECK(GetActiveHostStatus() == ActiveHostStatus::CONNECTED);
DCHECK(!GetTetherNetworkGuid().empty());
DCHECK(!GetWifiNetworkGuid().empty());
std::move(active_host_callback)
.Run(ActiveHostStatus::CONNECTED, active_host, GetTetherNetworkGuid(),
GetWifiNetworkGuid());
}
ActiveHost::ActiveHostStatus ActiveHost::GetActiveHostStatus() const {
return static_cast<ActiveHostStatus>(
pref_service_->GetInteger(prefs::kActiveHostStatus));
}
std::string ActiveHost::GetActiveHostDeviceId() const {
return pref_service_->GetString(prefs::kActiveHostDeviceId);
}
std::string ActiveHost::GetWifiNetworkGuid() const {
return pref_service_->GetString(prefs::kWifiNetworkGuid);
}
std::string ActiveHost::GetTetherNetworkGuid() const {
return pref_service_->GetString(prefs::kTetherNetworkGuid);
}
void ActiveHost::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void ActiveHost::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void ActiveHost::SetActiveHost(ActiveHostStatus active_host_status,
const std::string& active_host_device_id,
const std::string& tether_network_guid,
const std::string& wifi_network_guid) {
ActiveHostStatus old_status = GetActiveHostStatus();
std::string old_device_id = GetActiveHostDeviceId();
std::string old_tether_network_guid = GetTetherNetworkGuid();
std::string old_wifi_network_guid = GetWifiNetworkGuid();
if (old_status == active_host_status &&
old_device_id == active_host_device_id &&
old_tether_network_guid == tether_network_guid &&
old_wifi_network_guid == wifi_network_guid) {
// If nothing has changed, return early.
return;
}
pref_service_->Set(prefs::kActiveHostStatus,
base::Value(static_cast<int>(active_host_status)));
pref_service_->Set(prefs::kActiveHostDeviceId,
base::Value(active_host_device_id));
pref_service_->Set(prefs::kTetherNetworkGuid,
base::Value(tether_network_guid));
pref_service_->Set(prefs::kWifiNetworkGuid, base::Value(wifi_network_guid));
// Now, send an active host changed update.
GetActiveHost(base::BindOnce(&ActiveHost::SendActiveHostChangedUpdate,
weak_ptr_factory_.GetWeakPtr(), old_status,
old_device_id, old_tether_network_guid,
old_wifi_network_guid));
}
void ActiveHost::SendActiveHostChangedUpdate(
ActiveHostStatus old_status,
const std::string& old_active_host_id,
const std::string& old_tether_network_guid,
const std::string& old_wifi_network_guid,
ActiveHostStatus new_status,
std::optional<multidevice::RemoteDeviceRef> new_active_host,
const std::string& new_tether_network_guid,
const std::string& new_wifi_network_guid) {
PA_LOG(INFO) << "Active host changed from " << static_cast<int>(old_status)
<< " to " << static_cast<int>(new_status);
ActiveHostChangeInfo info;
info.new_status = new_status;
info.old_status = old_status;
info.new_active_host = new_active_host;
info.old_active_host_id = old_active_host_id;
info.old_tether_network_guid = old_tether_network_guid;
info.new_tether_network_guid = new_tether_network_guid;
info.new_wifi_network_guid = new_wifi_network_guid;
info.old_wifi_network_guid = old_wifi_network_guid;
for (auto& observer : observer_list_) {
PA_LOG(INFO) << "Notifying observer of active host change";
observer.OnActiveHostChanged(info);
}
}
} // namespace ash::tether