| // Copyright (c) 2012 The Chromium OS 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 "power_manager/powerd/system/dbus_wrapper_stub.h" |
| |
| #include <memory> |
| #include <tuple> |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/logging.h> |
| #include <base/memory/ptr_util.h> |
| #include <base/threading/thread_task_runner_handle.h> |
| #include <dbus/dbus.h> |
| |
| namespace power_manager { |
| namespace system { |
| |
| namespace { |
| |
| // Returns a copy of |signal|. |
| std::unique_ptr<dbus::Signal> DuplicateSignal(dbus::Signal* signal) { |
| return dbus::Signal::FromRawMessage(dbus_message_copy(signal->raw_message())); |
| } |
| |
| // Transfers |src_response| to |dest_response|. Passed as a response callback to |
| // exported methods. |
| void MoveResponse(std::unique_ptr<dbus::Response>* dest_response, |
| std::unique_ptr<dbus::Response> src_response) { |
| *dest_response = std::move(src_response); |
| } |
| |
| // Callback for CallMethodAsync() to pass |response| to |callback|. |
| void RunResponseCallback(dbus::ObjectProxy::ResponseCallback callback, |
| std::unique_ptr<dbus::Response> response) { |
| std::move(callback).Run(response.get()); |
| } |
| |
| } // namespace |
| |
| bool DBusWrapperStub::RegisteredSignalInfo::operator<( |
| const RegisteredSignalInfo& o) const { |
| return std::tie(proxy, interface_name, signal_name) < |
| std::tie(o.proxy, o.interface_name, o.signal_name); |
| } |
| |
| DBusWrapperStub::DBusWrapperStub() = default; |
| |
| DBusWrapperStub::~DBusWrapperStub() = default; |
| |
| std::string DBusWrapperStub::GetSentSignalName(size_t index) { |
| CHECK_LT(index, sent_signals_.size()); |
| return sent_signals_[index].signal_name; |
| } |
| |
| bool DBusWrapperStub::GetSentSignal(size_t index, |
| const std::string& expected_signal_name, |
| google::protobuf::MessageLite* protobuf_out, |
| std::unique_ptr<dbus::Signal>* signal_out) { |
| if (index >= sent_signals_.size()) { |
| LOG(ERROR) << "Got request to return " << expected_signal_name << " signal " |
| << "at position " << index << ", but only " |
| << sent_signals_.size() << " were sent"; |
| return false; |
| } |
| |
| SignalInfo& info = sent_signals_[index]; |
| if (info.signal_name != expected_signal_name) { |
| LOG(ERROR) << "Expected " << expected_signal_name << " signal at position " |
| << index << " but had " << info.signal_name << " instead"; |
| return false; |
| } |
| |
| if (protobuf_out) { |
| if (info.protobuf_type != protobuf_out->GetTypeName()) { |
| LOG(ERROR) << info.signal_name << " signal at position " << index |
| << " has " << info.protobuf_type << " protobuf instead of " |
| << "expected " << protobuf_out->GetTypeName(); |
| return false; |
| } |
| if (!protobuf_out->ParseFromString(info.serialized_data)) { |
| LOG(ERROR) << "Unable to parse " << info.protobuf_type |
| << " protobuf from " << info.signal_name |
| << " signal at position " << index; |
| return false; |
| } |
| } |
| |
| if (signal_out) { |
| if (!info.signal.get()) { |
| LOG(ERROR) << info.signal_name << " signal at position " << index |
| << " wasn't sent using EmitSignal()"; |
| return false; |
| } |
| *signal_out = DuplicateSignal(info.signal.get()); |
| } |
| |
| return true; |
| } |
| |
| void DBusWrapperStub::ClearSentSignals() { |
| sent_signals_.clear(); |
| } |
| |
| bool DBusWrapperStub::IsMethodExported(const std::string& method_name) const { |
| return exported_methods_.count(method_name); |
| } |
| |
| void DBusWrapperStub::CallExportedMethod( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_cb) { |
| CHECK(method_call); |
| |
| // libdbus asserts that the serial number is set. Prevent tests from needing |
| // to bother setting it. |
| method_call->SetSerial(1); |
| |
| const std::string name = method_call->GetMember(); |
| CHECK(exported_methods_.count(name)) << "Method " << name << " not exported"; |
| exported_methods_[name].Run(method_call, std::move(response_cb)); |
| } |
| |
| std::unique_ptr<dbus::Response> DBusWrapperStub::CallExportedMethodSync( |
| dbus::MethodCall* method_call) { |
| std::unique_ptr<dbus::Response> response; |
| CallExportedMethod(method_call, base::Bind(&MoveResponse, &response)); |
| return response; |
| } |
| |
| void DBusWrapperStub::EmitRegisteredSignal(dbus::ObjectProxy* proxy, |
| dbus::Signal* signal) { |
| CHECK(proxy); |
| CHECK(signal); |
| RegisteredSignalInfo info{proxy, signal->GetInterface(), signal->GetMember()}; |
| CHECK(signal_handlers_.count(info)) |
| << "No signal handler registered on " << proxy << " for " |
| << info.interface_name << "." << info.signal_name; |
| signal_handlers_[info].Run(signal); |
| } |
| |
| void DBusWrapperStub::SetMethodCallback(const MethodCallback& callback) { |
| method_callback_ = callback; |
| } |
| |
| void DBusWrapperStub::NotifyServiceAvailable(dbus::ObjectProxy* proxy, |
| bool available) { |
| auto it = service_availability_callbacks_.find(proxy); |
| if (it == service_availability_callbacks_.end()) |
| return; |
| |
| auto callbacks = std::move(it->second); |
| service_availability_callbacks_.erase(it); |
| for (auto& cb : callbacks) |
| std::move(cb).Run(available); |
| } |
| |
| void DBusWrapperStub::NotifyNameOwnerChanged(const std::string& service_name, |
| const std::string& old_owner, |
| const std::string& new_owner) { |
| for (DBusWrapperInterface::Observer& observer : observers_) |
| observer.OnDBusNameOwnerChanged(service_name, old_owner, new_owner); |
| } |
| |
| void DBusWrapperStub::AddObserver(Observer* observer) { |
| CHECK(observer); |
| observers_.AddObserver(observer); |
| } |
| |
| void DBusWrapperStub::RemoveObserver(Observer* observer) { |
| CHECK(observer); |
| observers_.RemoveObserver(observer); |
| } |
| |
| dbus::Bus* DBusWrapperStub::GetBus() { |
| return nullptr; |
| } |
| |
| dbus::ObjectProxy* DBusWrapperStub::GetObjectProxy( |
| const std::string& service_name, const std::string& object_path) { |
| // If a proxy was already created, return it. |
| for (const auto& info : object_proxy_infos_) { |
| if (info.service_name == service_name && info.object_path == object_path) { |
| return info.object_proxy.get(); |
| } |
| } |
| |
| // Ownership of this is passed to ObjectProxyInfo in the next statement. |
| dbus::ObjectProxy* object_proxy = new dbus::ObjectProxy( |
| nullptr, service_name, dbus::ObjectPath(object_path), 0); |
| object_proxy_infos_.emplace_back( |
| ObjectProxyInfo{service_name, object_path, object_proxy}); |
| return object_proxy; |
| } |
| |
| void DBusWrapperStub::RegisterForServiceAvailability( |
| dbus::ObjectProxy* proxy, |
| dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) { |
| DCHECK(proxy); |
| service_availability_callbacks_[proxy].push_back(std::move(callback)); |
| } |
| |
| void DBusWrapperStub::RegisterForSignal( |
| dbus::ObjectProxy* proxy, |
| const std::string& interface_name, |
| const std::string& signal_name, |
| dbus::ObjectProxy::SignalCallback callback) { |
| DCHECK(proxy); |
| RegisteredSignalInfo info{proxy, interface_name, signal_name}; |
| CHECK(!signal_handlers_.count(info)) |
| << "Signal handler already registered on " << proxy << " for " |
| << interface_name << "." << signal_name; |
| signal_handlers_[info] = callback; |
| } |
| |
| void DBusWrapperStub::ExportMethod( |
| const std::string& method_name, |
| dbus::ExportedObject::MethodCallCallback callback) { |
| CHECK(!service_published_) << "Method " << method_name |
| << " exported after service already published"; |
| CHECK(!exported_methods_.count(method_name)) |
| << "Method " << method_name << " exported twice"; |
| exported_methods_[method_name] = callback; |
| } |
| |
| bool DBusWrapperStub::PublishService() { |
| CHECK(!service_published_) << "Service already published"; |
| service_published_ = true; |
| return true; |
| } |
| |
| void DBusWrapperStub::EmitSignal(dbus::Signal* signal) { |
| DCHECK(signal); |
| sent_signals_.emplace_back( |
| SignalInfo{signal->GetMember(), DuplicateSignal(signal)}); |
| } |
| |
| void DBusWrapperStub::EmitBareSignal(const std::string& signal_name) { |
| sent_signals_.emplace_back(SignalInfo{signal_name}); |
| } |
| |
| void DBusWrapperStub::EmitSignalWithProtocolBuffer( |
| const std::string& signal_name, |
| const google::protobuf::MessageLite& protobuf) { |
| std::string serialized_data; |
| protobuf.SerializeToString(&serialized_data); |
| sent_signals_.emplace_back( |
| SignalInfo{signal_name, std::unique_ptr<dbus::Signal>(), |
| protobuf.GetTypeName(), serialized_data}); |
| } |
| |
| std::unique_ptr<dbus::Response> DBusWrapperStub::CallMethodSync( |
| dbus::ObjectProxy* proxy, |
| dbus::MethodCall* method_call, |
| base::TimeDelta timeout) { |
| DCHECK(proxy); |
| DCHECK(method_call); |
| DCHECK(!method_callback_.is_null()); |
| |
| // libdbus asserts that the serial number is set. Prevent tests from needing |
| // to bother setting it. |
| method_call->SetSerial(1); |
| return method_callback_.Run(proxy, method_call); |
| } |
| |
| void DBusWrapperStub::CallMethodAsync( |
| dbus::ObjectProxy* proxy, |
| dbus::MethodCall* method_call, |
| base::TimeDelta timeout, |
| dbus::ObjectProxy::ResponseCallback callback) { |
| // Call the method handler now and post |callback| to run later. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&RunResponseCallback, std::move(callback), |
| CallMethodSync(proxy, method_call, timeout))); |
| } |
| |
| } // namespace system |
| } // namespace power_manager |