| // 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. |
| #include "chromeos/dbus/system_proxy/system_proxy_client.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/optional.h" |
| #include "base/strings/strcat.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chromeos/dbus/system_proxy/fake_system_proxy_client.h" |
| #include "dbus/bus.h" |
| #include "dbus/message.h" |
| #include "dbus/object_proxy.h" |
| #include "third_party/cros_system_api/dbus/system_proxy/dbus-constants.h" |
| |
| namespace chromeos { |
| namespace { |
| |
| SystemProxyClient* g_instance = nullptr; |
| |
| const char kDbusCallFailure[] = "Failed to call system_proxy."; |
| const char kProtoMessageParsingFailure[] = |
| "Failed to parse response message from system_proxy."; |
| |
| // Tries to parse a proto message from |response| into |proto| and returns null |
| // if successful. If |response| is nullptr or the message cannot be parsed it |
| // will return an appropriate error message. |
| const char* DeserializeProto(dbus::Response* response, |
| google::protobuf::MessageLite* proto) { |
| if (!response) |
| return kDbusCallFailure; |
| |
| dbus::MessageReader reader(response); |
| if (!reader.PopArrayOfBytesAsProto(proto)) |
| return kProtoMessageParsingFailure; |
| |
| return nullptr; |
| } |
| |
| void OnSignalConnected(const std::string& interface_name, |
| const std::string& signal_name, |
| bool success) { |
| DCHECK_EQ(interface_name, system_proxy::kSystemProxyInterface); |
| if (!success) { |
| LOG(ERROR) << "Failed to connect to the System-proxy d-bus interface."; |
| } |
| } |
| |
| // "Real" implementation of SystemProxyClient talking to the SystemProxy daemon |
| // on the Chrome OS side. |
| class SystemProxyClientImpl : public SystemProxyClient { |
| public: |
| SystemProxyClientImpl() = default; |
| SystemProxyClientImpl(const SystemProxyClientImpl&) = delete; |
| SystemProxyClientImpl& operator=(const SystemProxyClientImpl&) = delete; |
| ~SystemProxyClientImpl() override = default; |
| |
| // SystemProxyClient overrides: |
| void SetAuthenticationDetails( |
| const system_proxy::SetAuthenticationDetailsRequest& request, |
| SetAuthenticationDetailsCallback callback) override { |
| CallProtoMethodWithRequest(system_proxy::kSetAuthenticationDetailsMethod, |
| request, std::move(callback)); |
| } |
| |
| void ShutDownDaemon(ShutDownDaemonCallback callback) override { |
| CallProtoMethod(system_proxy::kShutDownMethod, std::move(callback)); |
| } |
| |
| void ClearUserCredentials( |
| const system_proxy::ClearUserCredentialsRequest& request, |
| ClearUserCredentialsCallback callback) override { |
| CallProtoMethodWithRequest(system_proxy::kClearUserCredentialsMethod, |
| request, std::move(callback)); |
| } |
| |
| void SetWorkerActiveSignalCallback(WorkerActiveCallback callback) override { |
| DCHECK(callback); |
| DCHECK(!worker_active_callback_); |
| worker_active_callback_ = callback; |
| } |
| |
| void SetAuthenticationRequiredSignalCallback( |
| AuthenticationRequiredCallback callback) override { |
| DCHECK(callback); |
| DCHECK(!auth_required_callback_); |
| auth_required_callback_ = callback; |
| } |
| |
| void ConnectToWorkerSignals() override { |
| proxy_->WaitForServiceToBeAvailable( |
| base::BindOnce(&SystemProxyClientImpl::OnSystemProxyServiceAvailable, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void Init(dbus::Bus* bus) { |
| proxy_ = bus->GetObjectProxy( |
| system_proxy::kSystemProxyServiceName, |
| dbus::ObjectPath(system_proxy::kSystemProxyServicePath)); |
| } |
| |
| private: |
| TestInterface* GetTestInterface() override { return nullptr; } |
| |
| // Calls System-proxy's |method_name| method. Once the (asynchronous) call |
| // finishes, |callback| is called with the response proto (on the same thread |
| // as this call). |
| template <class TResponse> |
| void CallProtoMethod(const char* method_name, |
| base::OnceCallback<void(const TResponse&)> callback) { |
| dbus::MethodCall method_call(system_proxy::kSystemProxyInterface, |
| method_name); |
| dbus::MessageWriter writer(&method_call); |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&SystemProxyClientImpl::HandleResponse<TResponse>, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| // Same as |CallProtoMethod| but passes in |request| as input. |
| template <class TRequest, class TResponse> |
| void CallProtoMethodWithRequest( |
| const char* method_name, |
| const TRequest& request, |
| base::OnceCallback<void(const TResponse&)> callback) { |
| dbus::MethodCall method_call(system_proxy::kSystemProxyInterface, |
| method_name); |
| dbus::MessageWriter writer(&method_call); |
| |
| if (!writer.AppendProtoAsArrayOfBytes(request)) { |
| TResponse response; |
| response.set_error_message( |
| base::StrCat({"Failure to call d-bus method: ", method_name})); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), response)); |
| return; |
| } |
| |
| proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::BindOnce(&SystemProxyClientImpl::HandleResponse<TResponse>, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| // Parses the response proto message from |response| and calls |callback| with |
| // the decoded message. Calls |callback| with an |ERROR_DBUS_FAILURE| message |
| // on error. |
| template <class TProto> |
| void HandleResponse(base::OnceCallback<void(const TProto&)> callback, |
| dbus::Response* response) { |
| TProto response_proto; |
| const char* error_message = DeserializeProto(response, &response_proto); |
| if (error_message) { |
| response_proto.set_error_message(error_message); |
| } |
| std::move(callback).Run(response_proto); |
| } |
| |
| void OnWorkerActive(dbus::Signal* signal) { |
| DCHECK_EQ(signal->GetInterface(), system_proxy::kSystemProxyInterface); |
| DCHECK_EQ(signal->GetMember(), system_proxy::kWorkerActiveSignal); |
| |
| dbus::MessageReader signal_reader(signal); |
| system_proxy::WorkerActiveSignalDetails details; |
| if (!signal_reader.PopArrayOfBytesAsProto(&details)) { |
| LOG(ERROR) << "Failed to read connection details for active proxy " |
| "worker process."; |
| return; |
| } |
| if (!worker_active_callback_) { |
| LOG(WARNING) << "WorkerActive signal is ignored."; |
| return; |
| } |
| worker_active_callback_.Run(details); |
| } |
| |
| void OnAuthenticationRequired(dbus::Signal* signal) { |
| DCHECK_EQ(signal->GetInterface(), system_proxy::kSystemProxyInterface); |
| DCHECK_EQ(signal->GetMember(), system_proxy::kAuthenticationRequiredSignal); |
| |
| dbus::MessageReader signal_reader(signal); |
| system_proxy::AuthenticationRequiredDetails details; |
| if (!signal_reader.PopArrayOfBytesAsProto(&details)) { |
| LOG(ERROR) |
| << "Failed to read required authentication details from signal."; |
| return; |
| } |
| if (!auth_required_callback_) { |
| LOG(WARNING) << "AuthenticationRequired signal is ignored."; |
| return; |
| } |
| |
| auth_required_callback_.Run(details); |
| } |
| |
| void OnSystemProxyServiceAvailable(bool is_available) { |
| if (!is_available) { |
| LOG(ERROR) << "System-proxy service not available"; |
| return; |
| } |
| |
| proxy_->ConnectToSignal( |
| system_proxy::kSystemProxyInterface, system_proxy::kWorkerActiveSignal, |
| base::BindRepeating(&SystemProxyClientImpl::OnWorkerActive, |
| weak_factory_.GetWeakPtr()), |
| base::BindOnce(&OnSignalConnected)); |
| |
| proxy_->ConnectToSignal( |
| system_proxy::kSystemProxyInterface, |
| system_proxy::kAuthenticationRequiredSignal, |
| base::BindRepeating(&SystemProxyClientImpl::OnAuthenticationRequired, |
| weak_factory_.GetWeakPtr()), |
| base::BindOnce(&OnSignalConnected)); |
| } |
| |
| // Signal callbacks. |
| WorkerActiveCallback worker_active_callback_; |
| AuthenticationRequiredCallback auth_required_callback_; |
| |
| // D-Bus proxy for the SystemProxy daemon, not owned. |
| dbus::ObjectProxy* proxy_ = nullptr; |
| |
| base::WeakPtrFactory<SystemProxyClientImpl> weak_factory_{this}; |
| }; |
| |
| } // namespace |
| |
| SystemProxyClient::SystemProxyClient() { |
| CHECK(!g_instance); |
| g_instance = this; |
| } |
| |
| SystemProxyClient::~SystemProxyClient() { |
| CHECK_EQ(this, g_instance); |
| g_instance = nullptr; |
| } |
| |
| // static |
| void SystemProxyClient::Initialize(dbus::Bus* bus) { |
| CHECK(bus); |
| (new SystemProxyClientImpl())->Init(bus); |
| } |
| |
| // static |
| void SystemProxyClient::InitializeFake() { |
| new FakeSystemProxyClient(); |
| } |
| |
| // static |
| void SystemProxyClient::Shutdown() { |
| CHECK(g_instance); |
| delete g_instance; |
| } |
| |
| // static |
| SystemProxyClient* SystemProxyClient::Get() { |
| return g_instance; |
| } |
| |
| } // namespace chromeos |