1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
6 
7 #include <memory>
8 
9 #include "base/bind.h"
10 #include "base/memory/weak_ptr.h"
11 #include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h"
12 #include "dbus/bus.h"
13 #include "dbus/message.h"
14 #include "dbus/object_proxy.h"
15 #include "mojo/public/cpp/platform/platform_channel.h"
16 #include "mojo/public/cpp/system/invitation.h"
17 #include "third_party/cros_system_api/dbus/service_constants.h"
18 
19 namespace chromeos {
20 
21 namespace {
22 
23 CrosHealthdClient* g_instance = nullptr;
24 
25 // Production implementation of CrosHealthdClient.
26 class CrosHealthdClientImpl : public CrosHealthdClient {
27  public:
28   CrosHealthdClientImpl() = default;
29   ~CrosHealthdClientImpl() override = default;
30 
31   // CrosHealthdClient overrides:
32   mojo::Remote<cros_healthd::mojom::CrosHealthdServiceFactory>
BootstrapMojoConnection(BootstrapMojoConnectionCallback result_callback)33   BootstrapMojoConnection(
34       BootstrapMojoConnectionCallback result_callback) override {
35     // Invalidate any pending attempts to bootstrap the mojo connection.
36     bootstrap_weak_ptr_factory_.InvalidateWeakPtrs();
37 
38     mojo::PlatformChannel platform_channel;
39     // Prepare a Mojo invitation to send through |platform_channel|.
40     mojo::OutgoingInvitation invitation;
41     // Include an initial Mojo pipe in the invitation.
42     mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(
43         diagnostics::kCrosHealthdMojoConnectionChannelToken);
44     mojo::OutgoingInvitation::Send(std::move(invitation),
45                                    base::kNullProcessHandle,
46                                    platform_channel.TakeLocalEndpoint());
47 
48     // Bind our end of |pipe| to our CrosHealthdService remote. The daemon
49     // should bind its end to a CrosHealthdService implementation.
50     mojo::Remote<cros_healthd::mojom::CrosHealthdServiceFactory>
51         cros_healthd_service_factory;
52     cros_healthd_service_factory.Bind(
53         mojo::PendingRemote<cros_healthd::mojom::CrosHealthdServiceFactory>(
54             std::move(pipe), 0u /* version */));
55 
56     cros_healthd_service_proxy_->WaitForServiceToBeAvailable(base::BindOnce(
57         &CrosHealthdClientImpl::OnDbusServiceAvailable,
58         bootstrap_weak_ptr_factory_.GetWeakPtr(), std::move(result_callback),
59         std::move(platform_channel)));
60 
61     return cros_healthd_service_factory;
62   }
63 
Init(dbus::Bus * const bus)64   void Init(dbus::Bus* const bus) {
65     cros_healthd_service_proxy_ = bus->GetObjectProxy(
66         diagnostics::kCrosHealthdServiceName,
67         dbus::ObjectPath(diagnostics::kCrosHealthdServicePath));
68   }
69 
70  private:
71   dbus::ObjectProxy* cros_healthd_service_proxy_ = nullptr;
72 
73   // When the service is available, attempt to bootstrap the mojo connection.
OnDbusServiceAvailable(BootstrapMojoConnectionCallback result_callback,mojo::PlatformChannel platform_channel,bool success)74   void OnDbusServiceAvailable(BootstrapMojoConnectionCallback result_callback,
75                               mojo::PlatformChannel platform_channel,
76                               bool success) {
77     // The service is not available.
78     if (!success) {
79       std::move(result_callback).Run(false);
80       return;
81     }
82 
83     dbus::MethodCall method_call(
84         diagnostics::kCrosHealthdServiceInterface,
85         diagnostics::kCrosHealthdBootstrapMojoConnectionMethod);
86     dbus::MessageWriter writer(&method_call);
87     base::ScopedFD fd =
88         platform_channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD();
89     writer.AppendFileDescriptor(fd.get());
90     writer.AppendBool(/*is_chrome=*/true);
91     cros_healthd_service_proxy_->CallMethod(
92         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
93         base::BindOnce(
94             &CrosHealthdClientImpl::OnBootstrapMojoConnectionResponse,
95             bootstrap_weak_ptr_factory_.GetWeakPtr(),
96             std::move(result_callback)));
97   }
98 
99   // Passes the success/failure of |dbus_response| on to |result_callback|.
OnBootstrapMojoConnectionResponse(BootstrapMojoConnectionCallback result_callback,dbus::Response * const dbus_response)100   void OnBootstrapMojoConnectionResponse(
101       BootstrapMojoConnectionCallback result_callback,
102       dbus::Response* const dbus_response) {
103     const bool success = dbus_response != nullptr;
104     std::move(result_callback).Run(success);
105   }
106 
107   // Must be last class member. This WeakPtrFactory is specifically for the
108   // bootstrapping callbacks.
109   base::WeakPtrFactory<CrosHealthdClientImpl> bootstrap_weak_ptr_factory_{this};
110 
111   DISALLOW_COPY_AND_ASSIGN(CrosHealthdClientImpl);
112 };
113 
114 }  // namespace
115 
CrosHealthdClient()116 CrosHealthdClient::CrosHealthdClient() {
117   DCHECK(!g_instance);
118   g_instance = this;
119 }
120 
~CrosHealthdClient()121 CrosHealthdClient::~CrosHealthdClient() {
122   DCHECK_EQ(this, g_instance);
123   g_instance = nullptr;
124 }
125 
126 // static
Initialize(dbus::Bus * bus)127 void CrosHealthdClient::Initialize(dbus::Bus* bus) {
128   DCHECK(bus);
129   (new CrosHealthdClientImpl())->Init(bus);
130 }
131 
132 // static
InitializeFake()133 void CrosHealthdClient::InitializeFake() {
134   new cros_healthd::FakeCrosHealthdClient();
135 }
136 
137 // static
Shutdown()138 void CrosHealthdClient::Shutdown() {
139   DCHECK(g_instance);
140   delete g_instance;
141 }
142 
143 // static
Get()144 CrosHealthdClient* CrosHealthdClient::Get() {
145   return g_instance;
146 }
147 
148 }  // namespace chromeos
149