// Copyright 2013 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/dbus/shill/fake_shill_profile_client.h"
#include <memory>
#include <utility>
#include "base/containers/adapters.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "chromeos/ash/components/dbus/shill/shill_property_changed_observer.h"
#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/values_util.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace ash {
struct FakeShillProfileClient::ProfileProperties {
std::string profile_path;
// Dictionary of Service Dictionaries
base::Value::Dict entries;
// Dictionary of Profile properties
base::Value::Dict properties;
};
FakeShillProfileClient::FakeShillProfileClient() = default;
FakeShillProfileClient::~FakeShillProfileClient() = default;
void FakeShillProfileClient::AddPropertyChangedObserver(
const dbus::ObjectPath& profile_path,
ShillPropertyChangedObserver* observer) {}
void FakeShillProfileClient::RemovePropertyChangedObserver(
const dbus::ObjectPath& profile_path,
ShillPropertyChangedObserver* observer) {}
void FakeShillProfileClient::GetProperties(
const dbus::ObjectPath& profile_path,
base::OnceCallback<void(base::Value::Dict result)> callback,
ErrorCallback error_callback) {
ProfileProperties* profile = GetProfile(profile_path);
if (!profile) {
std::move(error_callback).Run("Error.InvalidProfile", "Invalid profile");
return;
}
base::Value::List entry_paths;
for (const auto it : profile->entries) {
entry_paths.Append(it.first);
}
base::Value::Dict properties = profile->properties.Clone();
properties.Set(shill::kEntriesProperty, std::move(entry_paths));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(properties)));
}
void FakeShillProfileClient::SetProperty(const dbus::ObjectPath& profile_path,
const std::string& name,
const base::Value& property,
base::OnceClosure callback,
ErrorCallback error_callback) {
ProfileProperties* profile = GetProfile(profile_path);
if (!profile) {
std::move(error_callback).Run("Error.InvalidProfile", "Invalid profile");
return;
}
profile->properties.Set(name, property.Clone());
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
void FakeShillProfileClient::SetObjectPathProperty(
const dbus::ObjectPath& profile_path,
const std::string& name,
const dbus::ObjectPath& property,
base::OnceClosure callback,
ErrorCallback error_callback) {
ProfileProperties* profile = GetProfile(profile_path);
if (!profile) {
std::move(error_callback).Run("Error.InvalidProfile", "Invalid profile");
return;
}
profile->properties.Set(name, property.value());
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
void FakeShillProfileClient::GetEntry(
const dbus::ObjectPath& profile_path,
const std::string& entry_path,
base::OnceCallback<void(base::Value::Dict result)> callback,
ErrorCallback error_callback) {
ProfileProperties* profile = GetProfile(profile_path);
if (!profile) {
std::move(error_callback).Run("Error.InvalidProfile", "Invalid profile");
return;
}
const base::Value::Dict* entry = profile->entries.FindDict(entry_path);
if (!entry) {
std::move(error_callback)
.Run("Error.InvalidProfileEntry", "Invalid profile entry");
return;
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), entry->Clone()));
}
void FakeShillProfileClient::DeleteEntry(const dbus::ObjectPath& profile_path,
const std::string& entry_path,
base::OnceClosure callback,
ErrorCallback error_callback) {
switch (simulate_delete_result_) {
case FakeShillSimulatedResult::kSuccess:
break;
case FakeShillSimulatedResult::kFailure:
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(error_callback), "Error",
"Simulated failure"));
return;
case FakeShillSimulatedResult::kTimeout:
// No callbacks get executed and the caller should eventually timeout.
return;
case FakeShillSimulatedResult::kInProgress:
// No callbacks get executed in this case.
return;
}
ProfileProperties* profile = GetProfile(profile_path);
if (!profile) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback), "Error.InvalidProfile",
profile_path.value()));
return;
}
if (!profile->entries.Remove(entry_path)) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(error_callback),
"Error.InvalidProfileEntry", entry_path));
return;
}
ShillServiceClient::Get()
->GetTestInterface()
->ClearConfiguredServiceProperties(entry_path);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
}
ShillProfileClient::TestInterface* FakeShillProfileClient::GetTestInterface() {
return this;
}
void FakeShillProfileClient::AddProfile(const std::string& profile_path,
const std::string& userhash) {
if (GetProfile(dbus::ObjectPath(profile_path)))
return;
// If adding a shared profile, make sure there are no user profiles currently
// on the stack - this assumes there is at most one shared profile.
CHECK(profile_path != GetSharedProfilePath() || profiles_.empty())
<< "Shared profile must be added before any user profile.";
ProfileProperties profile;
profile.properties.Set(shill::kUserHashProperty, userhash);
profile.profile_path = profile_path;
profiles_.push_back(std::move(profile));
ShillManagerClient::Get()->GetTestInterface()->AddProfile(profile_path);
}
void FakeShillProfileClient::AddEntry(const std::string& profile_path,
const std::string& entry_path,
const base::Value::Dict& properties) {
ProfileProperties* profile = GetProfile(dbus::ObjectPath(profile_path));
DCHECK(profile);
profile->entries.Set(entry_path, properties.Clone());
ShillManagerClient::Get()->GetTestInterface()->AddManagerService(entry_path,
true);
}
bool FakeShillProfileClient::AddService(const std::string& profile_path,
const std::string& service_path) {
ProfileProperties* profile = GetProfile(dbus::ObjectPath(profile_path));
if (!profile) {
LOG(ERROR) << "AddService: No matching profile: " << profile_path
<< " for: " << service_path;
return false;
}
if (profile->entries.contains(service_path)) {
return false;
}
return AddOrUpdateServiceImpl(profile_path, service_path, profile);
}
bool FakeShillProfileClient::UpdateService(const std::string& profile_path,
const std::string& service_path) {
ProfileProperties* profile = GetProfile(dbus::ObjectPath(profile_path));
if (!profile) {
LOG(ERROR) << "UpdateService: No matching profile: " << profile_path
<< " for: " << service_path;
return false;
}
if (!profile->entries.contains(service_path)) {
LOG(ERROR) << "UpdateService: Profile: " << profile_path
<< " does not contain Service: " << service_path;
return false;
}
return AddOrUpdateServiceImpl(profile_path, service_path, profile);
}
bool FakeShillProfileClient::AddOrUpdateServiceImpl(
const std::string& profile_path,
const std::string& service_path,
ProfileProperties* profile) {
ShillServiceClient::TestInterface* service_test =
ShillServiceClient::Get()->GetTestInterface();
const base::Value::Dict* service_properties =
service_test->GetServiceProperties(service_path);
if (!service_properties) {
LOG(ERROR) << "No matching service: " << service_path;
return false;
}
const std::string* service_profile_path =
service_properties->FindString(shill::kProfileProperty);
if (!service_profile_path || service_profile_path->empty()) {
base::Value profile_path_value(profile_path);
service_test->SetServiceProperty(service_path, shill::kProfileProperty,
profile_path_value);
} else if (*service_profile_path != profile_path) {
LOG(ERROR) << "Service has non matching profile path: "
<< *service_profile_path;
return false;
}
profile->entries.Set(service_path, service_properties->Clone());
return true;
}
void FakeShillProfileClient::GetProfilePaths(
std::vector<std::string>* profiles) {
for (const auto& profile : profiles_)
profiles->push_back(profile.profile_path);
}
void FakeShillProfileClient::GetProfilePathsContainingService(
const std::string& service_path,
std::vector<std::string>* profiles) {
for (const auto& profile : profiles_) {
if (profile.entries.FindDict(service_path)) {
profiles->push_back(profile.profile_path);
}
}
}
base::Value::Dict FakeShillProfileClient::GetProfileProperties(
const std::string& profile_path) {
ProfileProperties* profile = GetProfile(dbus::ObjectPath(profile_path));
DCHECK(profile);
return profile->properties.Clone();
}
std::optional<base::Value::Dict> FakeShillProfileClient::GetService(
const std::string& service_path,
std::string* profile_path) {
DCHECK(profile_path);
// Returns the entry added latest.
for (const auto& profile : base::Reversed(profiles_)) {
const base::Value::Dict* entry = profile.entries.FindDict(service_path);
if (!entry) {
continue;
}
*profile_path = profile.profile_path;
return entry->Clone();
}
return std::nullopt;
}
bool FakeShillProfileClient::HasService(const std::string& service_path) {
for (const auto& profile : profiles_) {
if (profile.entries.FindDict(service_path)) {
return true;
}
}
return false;
}
void FakeShillProfileClient::ClearProfiles() {
profiles_.clear();
}
void FakeShillProfileClient::SetSimulateDeleteResult(
FakeShillSimulatedResult delete_result) {
simulate_delete_result_ = delete_result;
}
FakeShillProfileClient::ProfileProperties* FakeShillProfileClient::GetProfile(
const dbus::ObjectPath& profile_path) {
for (auto& profile : profiles_) {
if (profile.profile_path == profile_path.value())
return &profile;
}
return nullptr;
}
} // namespace ash