Commit b5e2f635 authored by Azeem Arshad's avatar Azeem Arshad Committed by Commit Bot

[Cellular] Add Hermes DBus client classes.

This CL adds client class for Hermes Manager and Profile
objects. See http://go/cros-esim-design-doc for details.

Bug: 1093185
Change-Id: I4ca9d3da0d6e7b5d67ef27bf804a8de572c7ae44
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2238409
Commit-Queue: Azeem Arshad <azeemarshad@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#780043}
parent 6415d909
...@@ -140,6 +140,7 @@ source_set("chromeos") { ...@@ -140,6 +140,7 @@ source_set("chromeos") {
"//chromeos/dbus/cups_proxy", "//chromeos/dbus/cups_proxy",
"//chromeos/dbus/dlcservice", "//chromeos/dbus/dlcservice",
"//chromeos/dbus/dlcservice:dlcservice_proto", "//chromeos/dbus/dlcservice:dlcservice_proto",
"//chromeos/dbus/hermes",
"//chromeos/dbus/ip_peripheral", "//chromeos/dbus/ip_peripheral",
"//chromeos/dbus/kerberos", "//chromeos/dbus/kerberos",
"//chromeos/dbus/kerberos:kerberos_proto", "//chromeos/dbus/kerberos:kerberos_proto",
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "chromeos/dbus/cups_proxy/cups_proxy_client.h" #include "chromeos/dbus/cups_proxy/cups_proxy_client.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/dlcservice/dlcservice_client.h" #include "chromeos/dbus/dlcservice/dlcservice_client.h"
#include "chromeos/dbus/hermes/hermes_clients.h"
#include "chromeos/dbus/initialize_dbus_client.h" #include "chromeos/dbus/initialize_dbus_client.h"
#include "chromeos/dbus/ip_peripheral/ip_peripheral_service_client.h" #include "chromeos/dbus/ip_peripheral/ip_peripheral_service_client.h"
#include "chromeos/dbus/kerberos/kerberos_client.h" #include "chromeos/dbus/kerberos/kerberos_client.h"
...@@ -70,6 +71,7 @@ void InitializeDBus() { ...@@ -70,6 +71,7 @@ void InitializeDBus() {
InitializeDBusClient<CryptohomeClient>(bus); InitializeDBusClient<CryptohomeClient>(bus);
InitializeDBusClient<CupsProxyClient>(bus); InitializeDBusClient<CupsProxyClient>(bus);
InitializeDBusClient<DlcserviceClient>(bus); InitializeDBusClient<DlcserviceClient>(bus);
hermes_clients::Initialize(bus);
InitializeDBusClient<IpPeripheralServiceClient>(bus); InitializeDBusClient<IpPeripheralServiceClient>(bus);
InitializeDBusClient<KerberosClient>(bus); InitializeDBusClient<KerberosClient>(bus);
InitializeDBusClient<MachineLearningClient>(bus); InitializeDBusClient<MachineLearningClient>(bus);
...@@ -111,6 +113,7 @@ void ShutdownDBus() { ...@@ -111,6 +113,7 @@ void ShutdownDBus() {
MachineLearningClient::Shutdown(); MachineLearningClient::Shutdown();
KerberosClient::Shutdown(); KerberosClient::Shutdown();
IpPeripheralServiceClient::Shutdown(); IpPeripheralServiceClient::Shutdown();
hermes_clients::Shutdown();
DlcserviceClient::Shutdown(); DlcserviceClient::Shutdown();
CupsProxyClient::Shutdown(); CupsProxyClient::Shutdown();
CryptohomeClient::Shutdown(); CryptohomeClient::Shutdown();
......
...@@ -213,6 +213,7 @@ source_set("unit_tests") { ...@@ -213,6 +213,7 @@ source_set("unit_tests") {
"//chromeos/dbus/cryptohome", "//chromeos/dbus/cryptohome",
"//chromeos/dbus/cryptohome:attestation_proto", "//chromeos/dbus/cryptohome:attestation_proto",
"//chromeos/dbus/dlcservice:test_support", "//chromeos/dbus/dlcservice:test_support",
"//chromeos/dbus/hermes:test_support",
"//chromeos/dbus/ip_peripheral:test_support", "//chromeos/dbus/ip_peripheral:test_support",
"//chromeos/dbus/power:power_manager_proto", "//chromeos/dbus/power:power_manager_proto",
"//chromeos/dbus/power:test_support", "//chromeos/dbus/power:test_support",
......
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
component("hermes") {
defines = [ "IS_HERMES_CLIENT_IMPL" ]
deps = [
"//base",
"//chromeos/dbus:common",
"//chromeos/dbus/constants",
"//components/device_event_log",
"//dbus",
]
sources = [
"hermes_clients.cc",
"hermes_clients.h",
"hermes_manager_client.cc",
"hermes_manager_client.h",
"hermes_profile_client.cc",
"hermes_profile_client.h",
"hermes_response_status.cc",
"hermes_response_status.h",
]
}
source_set("test_support") {
testonly = true
public_deps = [ ":hermes" ]
deps = [
"//base",
"//base/test:test_support",
"//chromeos/dbus:common",
"//dbus:test_support",
"//testing/gmock",
"//testing/gtest",
]
sources = [
"hermes_client_test_base.cc",
"hermes_client_test_base.h",
"hermes_manager_client_unittest.cc",
"hermes_profile_client_unittest.cc",
"hermes_test_utils.cc",
"hermes_test_utils.h",
]
}
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/hermes/hermes_client_test_base.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/mock_bus.h"
namespace chromeos {
namespace {
void RunResponseOrErrorCallback(
dbus::ObjectProxy::ResponseOrErrorCallback callback,
std::unique_ptr<dbus::Response> response,
std::unique_ptr<dbus::ErrorResponse> error_response) {
std::move(callback).Run(response.get(), error_response.get());
}
} // namespace
HermesClientTestBase::HermesClientTestBase() = default;
HermesClientTestBase::~HermesClientTestBase() = default;
void HermesClientTestBase::OnMethodCalled(
dbus::MethodCall* method_call,
int timeout_ms,
dbus::ObjectProxy::ResponseOrErrorCallback* callback) {
ASSERT_FALSE(pending_method_call_results_.empty());
MethodCallResult result = std::move(pending_method_call_results_.front());
pending_method_call_results_.pop_front();
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&RunResponseOrErrorCallback, std::move(*callback),
std::move(result.first), std::move(result.second)));
}
void HermesClientTestBase::AddPendingMethodCallResult(
std::unique_ptr<dbus::Response> response,
std::unique_ptr<dbus::ErrorResponse> error_response) {
pending_method_call_results_.emplace_back(std::move(response),
std::move(error_response));
}
dbus::MockBus* HermesClientTestBase::GetMockBus() {
return bus_.get();
}
void HermesClientTestBase::InitMockBus() {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
bus_ = new dbus::MockBus(options);
}
} // namespace chromeos
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_DBUS_HERMES_HERMES_CLIENT_TEST_BASE_H_
#define CHROMEOS_DBUS_HERMES_HERMES_CLIENT_TEST_BASE_H_
#include "base/test/task_environment.h"
#include "dbus/object_proxy.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace dbus {
class MockBus;
} // namespace dbus
namespace chromeos {
// Base class for Hermes client unittests.
class HermesClientTestBase : public testing::Test {
public:
// Pops the top pending MethodCallResult and passes the response and error
// messages to callback. This is useful in EXPECT_CALL mock invocation of
// dbus::Bus::CallMethodWithErrorResponse.
void OnMethodCalled(dbus::MethodCall* method_call,
int timeout_ms,
dbus::ObjectProxy::ResponseOrErrorCallback* callback);
protected:
HermesClientTestBase();
~HermesClientTestBase() override;
// Queues a pending result that will be passed to the callback in a
// subsequent call to OnMethodCalled.
void AddPendingMethodCallResult(
std::unique_ptr<dbus::Response> response,
std::unique_ptr<dbus::ErrorResponse> error_response);
// Returns mock dbus::Bus instance.
dbus::MockBus* GetMockBus();
// Initialized mock bus instance.
void InitMockBus();
private:
// Mock bus for simulating calls.
scoped_refptr<dbus::MockBus> bus_;
using MethodCallResult = std::pair<std::unique_ptr<dbus::Response>,
std::unique_ptr<dbus::ErrorResponse>>;
std::deque<MethodCallResult> pending_method_call_results_;
base::test::SingleThreadTaskEnvironment task_environment_;
};
} // namespace chromeos
#endif // CHROMEOS_DBUS_HERMES_HERMES_CLIENT_TEST_BASE_H_
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/hermes/hermes_clients.h"
#include "chromeos/dbus/hermes/hermes_manager_client.h"
#include "chromeos/dbus/hermes/hermes_profile_client.h"
namespace chromeos {
namespace hermes_clients {
void Initialize(dbus::Bus* system_bus) {
#if !defined(USE_REAL_DBUS_CLIENTS)
if (!system_bus)
return InitializeFakes();
#endif
DCHECK(system_bus);
// The Hermes fake Manager client depends on fake Profile client
// to coordinate creating and managing of fake profile objects. The
// following makes sure that they are initialized in the correct order.
HermesProfileClient::Initialize(system_bus);
HermesManagerClient::Initialize(system_bus);
}
void InitializeFakes() {
HermesProfileClient::InitializeFake();
HermesManagerClient::InitializeFake();
}
void Shutdown() {
HermesManagerClient::Shutdown();
HermesProfileClient::Shutdown();
}
} // namespace hermes_clients
} // namespace chromeos
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_DBUS_HERMES_HERMES_CLIENTS_H_
#define CHROMEOS_DBUS_HERMES_HERMES_CLIENTS_H_
#include "base/component_export.h"
namespace dbus {
class Bus;
}
namespace chromeos {
namespace hermes_clients {
// Initializes all Hermes dbus clients in the correct order.
COMPONENT_EXPORT(HERMES_CLIENT) void Initialize(dbus::Bus* system_bus);
// Initializes fake implementations of all Hermes dbus clients.
COMPONENT_EXPORT(HERMES_CLIENT) void InitializeFakes();
// Shutdown all Hermes dbus clients.
COMPONENT_EXPORT(HERMES_CLIENT) void Shutdown();
} // namespace hermes_clients
} // namespace chromeos
#endif // CHROMEOS_DBUS_HERMES_HERMES_CLIENTS_H_
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/hermes/hermes_manager_client.h"
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "chromeos/dbus/hermes/hermes_response_status.h"
#include "components/device_event_log/device_event_log.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "dbus/property.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
namespace hermes {
namespace manager {
// TODO(crbug.com/1093185): Remove when hermes/dbus-constants.h is updated.
const char kPendingProfilesProperty[] = "PendingProfiles";
} // namespace manager
} // namespace hermes
namespace chromeos {
namespace {
HermesManagerClient* g_instance = nullptr;
} // namespace
// The HermesManagerClient implementation.
class HermesManagerClientImpl : public HermesManagerClient {
public:
explicit HermesManagerClientImpl(dbus::Bus* bus) {
dbus::ObjectPath hermes_manager_path(hermes::kHermesManagerPath);
object_proxy_ =
bus->GetObjectProxy(hermes::kHermesServiceName, hermes_manager_path);
properties_ = new Properties(
object_proxy_,
base::BindRepeating(&HermesManagerClientImpl::OnPropertyChanged,
weak_ptr_factory_.GetWeakPtr(),
hermes_manager_path));
properties_->ConnectSignals();
properties_->GetAll();
}
explicit HermesManagerClientImpl(const HermesManagerClient&) = delete;
~HermesManagerClientImpl() override = default;
// HermesManagerClient:
void InstallProfileFromActivationCode(
const std::string& activation_code,
const std::string& confirmation_code,
InstallCarrierProfileCallback callback) override {
dbus::MethodCall method_call(
hermes::kHermesManagerInterface,
hermes::manager::kInstallProfileFromActivationCode);
dbus::MessageWriter writer(&method_call);
writer.AppendString(activation_code);
writer.AppendString(confirmation_code);
object_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&HermesManagerClientImpl::OnProfileInstallResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void InstallPendingProfile(const dbus::ObjectPath& carrier_profile_path,
const std::string& confirmation_code,
InstallCarrierProfileCallback callback) override {
dbus::MethodCall method_call(hermes::kHermesManagerInterface,
hermes::manager::kInstallPendingProfile);
dbus::MessageWriter writer(&method_call);
writer.AppendObjectPath(carrier_profile_path);
writer.AppendString(confirmation_code);
object_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&HermesManagerClientImpl::OnProfileInstallResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void RequestPendingEvents(HermesResponseCallback callback) override {
dbus::MethodCall method_call(hermes::kHermesManagerInterface,
hermes::manager::kRequestPendingEvents);
object_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&HermesManagerClientImpl::OnHermesStatusResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void UninstallProfile(const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) override {
dbus::MethodCall method_call(hermes::kHermesManagerInterface,
hermes::manager::kUninstallProfile);
dbus::MessageWriter writer(&method_call);
writer.AppendObjectPath(carrier_profile_path);
object_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&HermesManagerClientImpl::OnHermesStatusResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
const std::vector<dbus::ObjectPath>& GetInstalledCarrierProfiles() override {
return properties_->installed_carrier_profiles().value();
}
const std::vector<dbus::ObjectPath>& GetPendingCarrierProfiles() override {
return properties_->pending_carrier_profiles().value();
}
TestInterface* GetTestInterface() override { return nullptr; }
HermesManagerClientImpl& operator=(const HermesManagerClient&) = delete;
private:
// Hermes Manager properties.
class Properties : public dbus::PropertySet {
public:
Properties(dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback& callback)
: dbus::PropertySet(object_proxy,
hermes::kHermesManagerInterface,
callback) {
RegisterProperty(hermes::manager::kProfilesProperty,
&installed_carrier_profiles_);
RegisterProperty(hermes::manager::kPendingProfilesProperty,
&pending_carrier_profiles_);
}
~Properties() override = default;
dbus::Property<std::vector<dbus::ObjectPath>>&
installed_carrier_profiles() {
return installed_carrier_profiles_;
}
dbus::Property<std::vector<dbus::ObjectPath>>& pending_carrier_profiles() {
return pending_carrier_profiles_;
}
private:
// List of paths to carrier profiles currently installed on the device.
dbus::Property<std::vector<dbus::ObjectPath>> installed_carrier_profiles_;
// List of pending carrier profiles from SMDS available for
// installation on this device.
dbus::Property<std::vector<dbus::ObjectPath>> pending_carrier_profiles_;
};
void OnPropertyChanged(const dbus::ObjectPath& object_path,
const std::string& property_name) {
if (property_name == hermes::manager::kProfilesProperty) {
for (auto& observer : observers()) {
observer.OnInstalledCarrierProfileListChanged();
}
} else {
for (auto& observer : observers()) {
observer.OnPendingCarrierProfileListChanged();
}
}
}
void OnProfileInstallResponse(InstallCarrierProfileCallback callback,
dbus::Response* response,
dbus::ErrorResponse* error_response) {
if (error_response) {
std::move(callback).Run(
HermesResponseStatusFromErrorName(error_response->GetErrorName()),
nullptr);
return;
}
if (!response) {
// No Error or Response received.
NET_LOG(ERROR) << "Carrier profile installation Error: No error or "
"response received.";
std::move(callback).Run(HermesResponseStatus::kErrorNoResponse, nullptr);
return;
}
dbus::MessageReader reader(response);
dbus::ObjectPath profile_path;
reader.PopObjectPath(&profile_path);
std::move(callback).Run(HermesResponseStatus::kSuccess, &profile_path);
}
void OnHermesStatusResponse(HermesResponseCallback callback,
dbus::Response* response,
dbus::ErrorResponse* error_response) {
if (error_response) {
std::move(callback).Run(
HermesResponseStatusFromErrorName(error_response->GetErrorName()));
return;
}
std::move(callback).Run(HermesResponseStatus::kSuccess);
}
dbus::ObjectProxy* object_proxy_;
Properties* properties_;
base::WeakPtrFactory<HermesManagerClientImpl> weak_ptr_factory_{this};
};
HermesManagerClient::HermesManagerClient() {
DCHECK(!g_instance);
g_instance = this;
}
HermesManagerClient::~HermesManagerClient() {
DCHECK_EQ(g_instance, this);
g_instance = nullptr;
}
void HermesManagerClient::AddObserver(HermesManagerClient::Observer* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void HermesManagerClient::RemoveObserver(
HermesManagerClient::Observer* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
// static
void HermesManagerClient::Initialize(dbus::Bus* bus) {
DCHECK(bus);
DCHECK(!g_instance);
new HermesManagerClientImpl(bus);
}
// static
void HermesManagerClient::InitializeFake() {
// TODO: Initialize Hermes Manager Fake.
}
// static
void HermesManagerClient::Shutdown() {
// TODO: DCHECK(g_instance);
delete g_instance;
}
// static
HermesManagerClient* HermesManagerClient::Get() {
return g_instance;
}
} // namespace chromeos
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_DBUS_HERMES_HERMES_MANAGER_CLIENT_H_
#define CHROMEOS_DBUS_HERMES_HERMES_MANAGER_CLIENT_H_
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "chromeos/dbus/dbus_method_call_status.h"
#include "chromeos/dbus/hermes/hermes_response_status.h"
#include "dbus/property.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
namespace dbus {
class Bus;
class ObjectPath;
} // namespace dbus
namespace chromeos {
// HermesManagerClient is used to talk to the main Hermes Manager dbus object.
class COMPONENT_EXPORT(HERMES_CLIENT) HermesManagerClient {
public:
// Callback for profile installation methods. Callback returns status code
// and the object path for the profile that was just successfully installed.
using InstallCarrierProfileCallback =
base::OnceCallback<void(HermesResponseStatus status,
const dbus::ObjectPath* carrier_profile_path)>;
// Interface for setting up and manipulating profiles in a testing
// environment.
class TestInterface {
public:
// Adds a new carrier profile with given path and properties.
virtual void AddCarrierProfile(const dbus::ObjectPath& path,
const std::string& iccid,
const std::string& name,
const std::string& service_provider,
const std::string& activation_code,
const std::string& network_service_path,
hermes::profile::State state) = 0;
};
// Interface for observing Hermes Manager changes.
class Observer {
public:
virtual ~Observer() = default;
// Called when new profiles are installed or removed.
virtual void OnInstalledCarrierProfileListChanged() {}
// Called when new pending profile list is updated.
virtual void OnPendingCarrierProfileListChanged() {}
};
// Adds an observer for carrier profile lists changes on Hermes manager.
virtual void AddObserver(Observer* observer);
// Removes an observer for Hermes manager.
virtual void RemoveObserver(Observer* observer);
// Install a carrier profile given the |activation_code| and
// |conirmation_code|. |confirmation_code| can be empty if no confirmation is
// required by carrier. Returns the object path to the carrier profile that
// was just installed.
virtual void InstallProfileFromActivationCode(
const std::string& activation_code,
const std::string& confirmation_code,
InstallCarrierProfileCallback callback) = 0;
// Installs a pending profile with given |carrier_profile_path|.
// |confirmation_code| can be empty if no confirmation is required by carrier.
// Returns the object path to the carrier profile that was just installed.
virtual void InstallPendingProfile(
const dbus::ObjectPath& carrier_profile_path,
const std::string& confirmation_code,
InstallCarrierProfileCallback callback) = 0;
// Updates pending profiles for the device from the SMDS server. This updates
// pending profiles list prior to returning.
virtual void RequestPendingEvents(HermesResponseCallback callback) = 0;
// Removes the carrier profile with the given |carrier_profile_path| from
// the device. Returns a response status indicating the result of the
// operation.
virtual void UninstallProfile(const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) = 0;
// Returns the list of all installed carrier profiles.
virtual const std::vector<dbus::ObjectPath>&
GetInstalledCarrierProfiles() = 0;
// Returns the list of carrier profiles that are available for installation.
virtual const std::vector<dbus::ObjectPath>& GetPendingCarrierProfiles() = 0;
// Returns an instance of Hermes Manager Test interface.
virtual TestInterface* GetTestInterface() = 0;
// Creates and initializes the global instance.
static void Initialize(dbus::Bus* bus);
// Creates and initializes a global fake instance.
static void InitializeFake();
// Destroys the global instance.
static void Shutdown();
// Returns the global instance.
static HermesManagerClient* Get();
protected:
HermesManagerClient();
virtual ~HermesManagerClient();
const base::ObserverList<HermesManagerClient::Observer>::Unchecked&
observers() {
return observers_;
}
private:
base::ObserverList<HermesManagerClient::Observer>::Unchecked observers_;
};
} // namespace chromeos
#endif // CHROMEOS_DBUS_HERMES_HERMES_MANAGER_CLIENT_H_
This diff is collapsed.
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/hermes/hermes_profile_client.h"
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/dbus/hermes/hermes_response_status.h"
#include "dbus/bus.h"
#include "dbus/object_manager.h"
#include "dbus/property.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
namespace chromeos {
namespace {
HermesProfileClient* g_instance = nullptr;
} // namespace
HermesProfileClient::Properties::Properties(
dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback& callback)
: dbus::PropertySet(object_proxy,
hermes::kHermesProfileInterface,
callback) {
RegisterProperty(hermes::profile::kIccidProperty, &iccid_);
RegisterProperty(hermes::profile::kServiceProviderProperty,
&service_provider_);
RegisterProperty(hermes::profile::kMccMncProperty, &mcc_mnc_);
RegisterProperty(hermes::profile::kActivationCodeProperty, &activation_code_);
RegisterProperty(hermes::profile::kNameProperty, &name_);
RegisterProperty(hermes::profile::kNicknameProperty, &nick_name_);
RegisterProperty(hermes::profile::kStateProperty, &state_);
RegisterProperty(hermes::profile::kProfileClassProperty, &profile_class_);
}
HermesProfileClient::Properties::~Properties() = default;
class HermesProfileClientImpl : public HermesProfileClient {
public:
explicit HermesProfileClientImpl(dbus::Bus* bus) : bus_(bus) {}
explicit HermesProfileClientImpl(const HermesProfileClient&) = delete;
~HermesProfileClientImpl() override = default;
using Object = std::pair<dbus::ObjectProxy*, Properties*>;
using ObjectMap = std::map<dbus::ObjectPath, Object>;
// HermesProfileClient:
void EnableCarrierProfile(const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) override {
dbus::MethodCall method_call(hermes::kHermesProfileInterface,
hermes::profile::kEnable);
dbus::ObjectProxy* object_proxy = GetObject(carrier_profile_path).first;
object_proxy->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&HermesProfileClientImpl::OnHermesStatusResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void DisableCarrierProfile(const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) override {
dbus::MethodCall method_call(hermes::kHermesProfileInterface,
hermes::profile::kDisable);
dbus::ObjectProxy* object_proxy = GetObject(carrier_profile_path).first;
object_proxy->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&HermesProfileClientImpl::OnHermesStatusResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
Properties* GetProperties(
const dbus::ObjectPath& carrier_profile_path) override {
return GetObject(carrier_profile_path).second;
}
HermesProfileClient& operator=(const HermesProfileClient&) = delete;
private:
Object GetObject(const dbus::ObjectPath& object_path) {
ObjectMap::iterator it = object_map_.find(object_path);
if (it != object_map_.end())
return it->second;
dbus::ObjectProxy* object_proxy =
bus_->GetObjectProxy(hermes::kHermesServiceName, object_path);
Properties* properties = new Properties(
object_proxy,
base::BindRepeating(&HermesProfileClientImpl::OnPropertyChanged,
weak_ptr_factory_.GetWeakPtr(), object_path));
properties->ConnectSignals();
properties->GetAll();
Object object = std::make_pair(object_proxy, properties);
object_map_[object_path] = object;
return object;
}
void OnPropertyChanged(const dbus::ObjectPath& object_path,
const std::string& property_name) {
for (auto& observer : observers()) {
observer.OnCarrierProfilePropertyChanged(object_path, property_name);
}
}
void OnHermesStatusResponse(HermesResponseCallback callback,
dbus::Response* response,
dbus::ErrorResponse* error_response) {
if (error_response) {
std::move(callback).Run(
HermesResponseStatusFromErrorName(error_response->GetErrorName()));
return;
}
std::move(callback).Run(HermesResponseStatus::kSuccess);
}
dbus::Bus* bus_;
ObjectMap object_map_;
base::WeakPtrFactory<HermesProfileClientImpl> weak_ptr_factory_{this};
};
HermesProfileClient::HermesProfileClient() {
DCHECK(!g_instance);
g_instance = this;
}
HermesProfileClient::~HermesProfileClient() {
DCHECK_EQ(g_instance, this);
g_instance = nullptr;
}
void HermesProfileClient::AddObserver(Observer* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void HermesProfileClient::RemoveObserver(Observer* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
// static
void HermesProfileClient::Initialize(dbus::Bus* bus) {
DCHECK(bus);
DCHECK(!g_instance);
new HermesProfileClientImpl(bus);
}
// static
void HermesProfileClient::InitializeFake() {
// TODO: Initialize Hermes Profile Fake.
}
// static
void HermesProfileClient::Shutdown() {
// TODO: DCHECK(g_instance);
delete g_instance;
}
// static
HermesProfileClient* HermesProfileClient::Get() {
return g_instance;
}
} // namespace chromeos
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_DBUS_HERMES_HERMES_PROFILE_CLIENT_H_
#define CHROMEOS_DBUS_HERMES_HERMES_PROFILE_CLIENT_H_
#include <string>
#include "base/component_export.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "chromeos/dbus/hermes/hermes_response_status.h"
#include "dbus/property.h"
namespace dbus {
class Bus;
class ObjectPath;
class ObjectProxy;
} // namespace dbus
namespace chromeos {
// HermesProfileClient is used to talk to Hermes profile objects.
class COMPONENT_EXPORT(HERMES_CLIENT) HermesProfileClient {
public:
// Hermes profile properties.
class Properties : public dbus::PropertySet {
public:
Properties(dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback& callback);
~Properties() override;
dbus::Property<std::string>& iccid() { return iccid_; }
dbus::Property<std::string>& service_provider() {
return service_provider_;
}
dbus::Property<std::string>& mcc_mnc() { return mcc_mnc_; }
dbus::Property<std::string>& activation_code() { return activation_code_; }
dbus::Property<std::string>& name() { return name_; }
dbus::Property<std::string>& nick_name() { return nick_name_; }
dbus::Property<int32_t>& state() { return state_; }
dbus::Property<int32_t>& profile_class() { return profile_class_; }
private:
dbus::Property<std::string> iccid_;
dbus::Property<std::string> service_provider_;
dbus::Property<std::string> mcc_mnc_;
dbus::Property<std::string> activation_code_;
dbus::Property<std::string> name_;
dbus::Property<std::string> nick_name_;
dbus::Property<int32_t> state_;
dbus::Property<int32_t> profile_class_;
};
// Interface for observing changes to profile objects.
class Observer {
public:
virtual ~Observer() = default;
// Called when a carrier profile property changes.
virtual void OnCarrierProfilePropertyChanged(
const dbus::ObjectPath& object_path,
const std::string& property_name) = 0;
};
// Adds an observer for changes to Hermes profile objects.
virtual void AddObserver(Observer* observer);
// Removes an observer for Hermes profile objects.
virtual void RemoveObserver(Observer* observer);
// Enables eSIM carrier profile with given |carrier_profile_path|. |callback|
// will receive status code indicating response status.
virtual void EnableCarrierProfile(
const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) = 0;
// Disables eSIM carrier profile with given |carrier_profile_path|. |callback|
// will receive status code indicating response status.
virtual void DisableCarrierProfile(
const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) = 0;
// Returns properties for eSIM carrier profile with given
// |carrier_profile_path|.
virtual Properties* GetProperties(
const dbus::ObjectPath& carrier_profile_path) = 0;
// Creates and initializes the global instance.
static void Initialize(dbus::Bus* bus);
// Creates and initializes a global fake instance.
static void InitializeFake();
// Destroys the global instance.
static void Shutdown();
// Returns the global instance.
static HermesProfileClient* Get();
protected:
HermesProfileClient();
virtual ~HermesProfileClient();
const base::ObserverList<HermesProfileClient::Observer>::Unchecked&
observers() {
return observers_;
}
private:
base::ObserverList<HermesProfileClient::Observer>::Unchecked observers_;
};
} // namespace chromeos
#endif // CHROMEOS_DBUS_HERMES_HERMES_PROFILE_CLIENT_H_
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/hermes/hermes_profile_client.h"
#include "chromeos/dbus/hermes/hermes_client_test_base.h"
#include "chromeos/dbus/hermes/hermes_response_status.h"
#include "chromeos/dbus/hermes/hermes_test_utils.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_object_proxy.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
namespace chromeos {
namespace {
const char* kTestProfilePath = "/org/chromium/hermes/Profile/1";
} // namespace
class HermesProfileClientTest : public HermesClientTestBase {
public:
HermesProfileClientTest() = default;
HermesProfileClientTest(const HermesProfileClientTest&) = delete;
~HermesProfileClientTest() override = default;
void SetUp() override {
InitMockBus();
dbus::ObjectPath test_profile_path(kTestProfilePath);
proxy_ = new dbus::MockObjectProxy(GetMockBus(), hermes::kHermesServiceName,
test_profile_path);
EXPECT_CALL(*GetMockBus(),
GetObjectProxy(hermes::kHermesServiceName, test_profile_path))
.WillRepeatedly(Return(proxy_.get()));
HermesProfileClient::Initialize(GetMockBus());
client_ = HermesProfileClient::Get();
base::RunLoop().RunUntilIdle();
}
void TearDown() override { HermesProfileClient::Shutdown(); }
HermesProfileClientTest& operator=(const HermesProfileClientTest&) = delete;
protected:
scoped_refptr<dbus::MockObjectProxy> proxy_;
HermesProfileClient* client_;
};
TEST_F(HermesProfileClientTest, TestEnableProfile) {
dbus::ObjectPath test_profile_path(kTestProfilePath);
dbus::MethodCall method_call(hermes::kHermesProfileInterface,
hermes::profile::kEnable);
method_call.SetSerial(123);
EXPECT_CALL(
*proxy_.get(),
DoCallMethodWithErrorResponse(
hermes_test_utils::MatchMethodName(hermes::profile::kEnable), _, _))
.Times(2)
.WillRepeatedly(Invoke(this, &HermesProfileClientTest::OnMethodCalled));
// Verify that client makes corresponding dbus method call with
// correct arguments.
HermesResponseStatus status;
AddPendingMethodCallResult(dbus::Response::CreateEmpty(), nullptr);
client_->EnableCarrierProfile(
test_profile_path,
base::BindOnce(&hermes_test_utils::CopyHermesStatus, &status));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(status, HermesResponseStatus::kSuccess);
// Verify that error responses are returned properly.
std::unique_ptr<dbus::ErrorResponse> error_response =
dbus::ErrorResponse::FromMethodCall(&method_call,
hermes::kErrorAlreadyEnabled, "");
AddPendingMethodCallResult(nullptr, std::move(error_response));
client_->EnableCarrierProfile(
test_profile_path,
base::BindOnce(&hermes_test_utils::CopyHermesStatus, &status));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(status, HermesResponseStatus::kErrorAlreadyEnabled);
}
TEST_F(HermesProfileClientTest, TestDisableProfile) {
dbus::ObjectPath test_profile_path(kTestProfilePath);
dbus::MethodCall method_call(hermes::kHermesProfileInterface,
hermes::profile::kDisable);
method_call.SetSerial(123);
EXPECT_CALL(
*proxy_.get(),
DoCallMethodWithErrorResponse(
hermes_test_utils::MatchMethodName(hermes::profile::kDisable), _, _))
.Times(2)
.WillRepeatedly(Invoke(this, &HermesProfileClientTest::OnMethodCalled));
// Verify that client makes corresponding dbus method call with
// correct arguments.
HermesResponseStatus status;
AddPendingMethodCallResult(dbus::Response::CreateEmpty(), nullptr);
client_->DisableCarrierProfile(
test_profile_path,
base::BindOnce(&hermes_test_utils::CopyHermesStatus, &status));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(status, HermesResponseStatus::kSuccess);
// Verify that error responses are returned properly.
std::unique_ptr<dbus::ErrorResponse> error_response =
dbus::ErrorResponse::FromMethodCall(&method_call,
hermes::kErrorAlreadyDisabled, "");
AddPendingMethodCallResult(nullptr, std::move(error_response));
client_->DisableCarrierProfile(
test_profile_path,
base::BindOnce(&hermes_test_utils::CopyHermesStatus, &status));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(status, HermesResponseStatus::kErrorAlreadyDisabled);
}
} // namespace chromeos
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/hermes/hermes_response_status.h"
namespace chromeos {
HermesResponseStatus HermesResponseStatusFromErrorName(
const std::string& error_name) {
if (error_name == "org.chromium.Hermes.Error.AlreadyDisabled") {
return HermesResponseStatus::kErrorAlreadyDisabled;
}
if (error_name == "org.chromium.Hermes.Error.AlreadyEnabled") {
return HermesResponseStatus::kErrorAlreadyEnabled;
}
if (error_name == "org.chromium.Hermes.Error.InvalidActivationCode") {
return HermesResponseStatus::kErrorInvalidActivationCode;
}
if (error_name == "org.chromium.Hermes.Error.InvalidIccid") {
return HermesResponseStatus::kErrorInvalidIccid;
}
if (error_name == "org.chromium.Hermes.Error.InvalidParameter") {
return HermesResponseStatus::kErrorInvalidParameter;
}
if (error_name == "org.chromium.Hermes.Error.NeedConfirmationCode") {
return HermesResponseStatus::kErrorNeedConfirmationCode;
}
if (error_name == "org.chromium.Hermes.Error.SendNotificationFailure") {
return HermesResponseStatus::kErrorSendNotificationFailure;
}
if (error_name == "org.chromium.Hermes.Error.TestProfileInProd") {
return HermesResponseStatus::kErrorTestProfileInProd;
}
if (error_name == "org.chromium.Hermes.Error.Unsupported") {
return HermesResponseStatus::kErrorUnsupported;
}
if (error_name == "org.chromium.Hermes.Error.WrongState") {
return HermesResponseStatus::kErrorWrongState;
}
return HermesResponseStatus::kErrorUnknown;
}
} // namespace chromeos
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_DBUS_HERMES_HERMES_RESPONSE_STATUS_H_
#define CHROMEOS_DBUS_HERMES_HERMES_RESPONSE_STATUS_H_
#include <string>
#include "base/callback.h"
namespace chromeos {
// Enum values the represent response status of hermes client method calls.
enum class HermesResponseStatus {
kSuccess,
kErrorAlreadyDisabled,
kErrorAlreadyEnabled,
kErrorInvalidActivationCode,
kErrorInvalidIccid,
kErrorInvalidParameter,
kErrorNeedConfirmationCode,
kErrorSendNotificationFailure,
kErrorTestProfileInProd,
kErrorUnknown,
kErrorUnsupported,
kErrorWrongState,
kErrorInvalidResponse,
kErrorNoResponse,
};
// Callback that receives only a HermesResponseStatus.
using HermesResponseCallback =
base::OnceCallback<void(HermesResponseStatus status)>;
// Returns the HermesResponseStatus corresponding to the
// given dbus_|error_name|.
HermesResponseStatus HermesResponseStatusFromErrorName(
const std::string& error_name);
} // namespace chromeos
#endif // CHROMEOS_DBUS_HERMES_HERMES_RESPONSE_STATUS_H_
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/hermes/hermes_test_utils.h"
#include "chromeos/dbus/hermes/hermes_response_status.h"
namespace chromeos {
namespace hermes_test_utils {
void CopyHermesStatus(HermesResponseStatus* dest_status,
HermesResponseStatus status) {
*dest_status = status;
}
} // namespace hermes_test_utils
} // namespace chromeos
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_DBUS_HERMES_HERMES_TEST_UTILS_H_
#define CHROMEOS_DBUS_HERMES_HERMES_TEST_UTILS_H_
#include "testing/gmock/include/gmock/gmock-matchers.h"
namespace chromeos {
enum class HermesResponseStatus;
namespace hermes_test_utils {
// Matches dbus::MethodCall with given method name.
MATCHER_P(MatchMethodName, method_name, "") {
if (arg->GetMember() != method_name) {
*result_listener << "has method_name=" << arg->GetMember();
return false;
}
return true;
}
// Copies |status| to |dest_status|. Useful as test callback to capture
// results for methods that return only a status
void CopyHermesStatus(HermesResponseStatus* dest_status,
HermesResponseStatus status);
} // namespace hermes_test_utils
} // namespace chromeos
#endif // CHROMEOS_DBUS_HERMES_HERMES_TEST_UTILS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment