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 "remoting/signaling/ftl_signal_strategy.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/observer_list.h"
14 #include "base/rand_util.h"
15 #include "base/sequence_checker.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/threading/sequenced_task_runner_handle.h"
18 #include "remoting/base/logging.h"
19 #include "remoting/base/oauth_token_getter.h"
20 #include "remoting/base/protobuf_http_status.h"
21 #include "remoting/signaling/ftl_device_id_provider.h"
22 #include "remoting/signaling/ftl_messaging_client.h"
23 #include "remoting/signaling/ftl_registration_manager.h"
24 #include "remoting/signaling/signaling_address.h"
25 #include "services/network/public/cpp/shared_url_loader_factory.h"
26 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
27 #include "third_party/libjingle_xmpp/xmpp/constants.h"
28 
29 namespace remoting {
30 
31 class FtlSignalStrategy::Core {
32  public:
33   Core(std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
34        std::unique_ptr<RegistrationManager> registration_manager,
35        std::unique_ptr<MessagingClient> messaging_client);
36   ~Core();
37 
38   void Connect();
39   void Disconnect();
40   State GetState() const;
41   Error GetError() const;
42   const SignalingAddress& GetLocalAddress() const;
43   void AddListener(Listener* listener);
44   void RemoveListener(Listener* listener);
45   bool SendStanza(std::unique_ptr<jingle_xmpp::XmlElement> stanza);
46   bool SendMessage(const SignalingAddress& destination_address,
47                    const ftl::ChromotingMessage& message);
48   bool IsSignInError() const;
49 
50  private:
51   // Methods are called in the order below when Connect() is called.
52   void OnGetOAuthTokenResponse(OAuthTokenGetter::Status status,
53                                const std::string& user_email,
54                                const std::string& access_token);
55   void OnSignInGaiaResponse(const ProtobufHttpStatus& status);
56   void StartReceivingMessages();
57   void OnReceiveMessagesStreamStarted();
58   void OnReceiveMessagesStreamClosed(const ProtobufHttpStatus& status);
59   void OnMessageReceived(const ftl::Id& sender_id,
60                          const std::string& sender_registration_id,
61                          const ftl::ChromotingMessage& message);
62 
63   void SendMessageImpl(const SignalingAddress& receiver,
64                        const ftl::ChromotingMessage& message,
65                        MessagingClient::DoneCallback callback);
66   void OnSendMessageResponse(const SignalingAddress& receiver,
67                              const std::string& stanza_id,
68                              const ProtobufHttpStatus& status);
69 
70   // Returns true if the status is handled.
71   void HandleProtobufHttpStatusError(const base::Location& location,
72                                      const ProtobufHttpStatus& status);
73 
74   void OnStanza(const SignalingAddress& sender_address,
75                 std::unique_ptr<jingle_xmpp::XmlElement> stanza);
76 
77   std::unique_ptr<OAuthTokenGetter> oauth_token_getter_;
78 
79   std::unique_ptr<RegistrationManager> registration_manager_;
80   std::unique_ptr<MessagingClient> messaging_client_;
81 
82   std::string user_email_;
83   SignalingAddress local_address_;
84 
85   std::unique_ptr<MessagingClient::MessageCallbackSubscription>
86       receive_message_subscription_;
87 
88   Error error_ = OK;
89   bool is_sign_in_error_ = false;
90 
91   base::ObserverList<Listener, true> listeners_;
92 
93   SEQUENCE_CHECKER(sequence_checker_);
94 
95   base::WeakPtrFactory<Core> weak_factory_{this};
96   DISALLOW_COPY_AND_ASSIGN(Core);
97 };
98 
Core(std::unique_ptr<OAuthTokenGetter> oauth_token_getter,std::unique_ptr<RegistrationManager> registration_manager,std::unique_ptr<MessagingClient> messaging_client)99 FtlSignalStrategy::Core::Core(
100     std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
101     std::unique_ptr<RegistrationManager> registration_manager,
102     std::unique_ptr<MessagingClient> messaging_client) {
103   DCHECK(oauth_token_getter);
104   DCHECK(registration_manager);
105   DCHECK(messaging_client);
106   oauth_token_getter_ = std::move(oauth_token_getter);
107   registration_manager_ = std::move(registration_manager);
108   messaging_client_ = std::move(messaging_client);
109 }
110 
~Core()111 FtlSignalStrategy::Core::~Core() {
112   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
113   Disconnect();
114 }
115 
Connect()116 void FtlSignalStrategy::Core::Connect() {
117   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
118 
119   if (GetState() != DISCONNECTED) {
120     LOG(WARNING) << "Signaling is not disconnected. State: " << GetState();
121     return;
122   }
123 
124   error_ = OK;
125   is_sign_in_error_ = false;
126 
127   receive_message_subscription_ =
128       messaging_client_->RegisterMessageCallback(base::BindRepeating(
129           &Core::OnMessageReceived, weak_factory_.GetWeakPtr()));
130 
131   for (auto& observer : listeners_)
132     observer.OnSignalStrategyStateChange(CONNECTING);
133 
134   StartReceivingMessages();
135 }
136 
Disconnect()137 void FtlSignalStrategy::Core::Disconnect() {
138   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
139 
140   if (registration_manager_->IsSignedIn()) {
141     registration_manager_->SignOut();
142   }
143 
144   if (receive_message_subscription_) {
145     local_address_ = SignalingAddress();
146     receive_message_subscription_.reset();
147     messaging_client_->StopReceivingMessages();
148 
149     for (auto& observer : listeners_)
150       observer.OnSignalStrategyStateChange(DISCONNECTED);
151   }
152 }
153 
GetState() const154 SignalStrategy::State FtlSignalStrategy::Core::GetState() const {
155   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
156 
157   if (!local_address_.empty()) {
158     DCHECK(receive_message_subscription_);
159     return CONNECTED;
160   } else if (receive_message_subscription_) {
161     return CONNECTING;
162   } else {
163     return DISCONNECTED;
164   }
165 }
166 
GetError() const167 SignalStrategy::Error FtlSignalStrategy::Core::GetError() const {
168   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
169   return error_;
170 }
171 
GetLocalAddress() const172 const SignalingAddress& FtlSignalStrategy::Core::GetLocalAddress() const {
173   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
174   return local_address_;
175 }
176 
AddListener(Listener * listener)177 void FtlSignalStrategy::Core::AddListener(Listener* listener) {
178   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
179   listeners_.AddObserver(listener);
180 }
181 
RemoveListener(Listener * listener)182 void FtlSignalStrategy::Core::RemoveListener(Listener* listener) {
183   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
184   listeners_.RemoveObserver(listener);
185 }
186 
SendStanza(std::unique_ptr<jingle_xmpp::XmlElement> stanza)187 bool FtlSignalStrategy::Core::SendStanza(
188     std::unique_ptr<jingle_xmpp::XmlElement> stanza) {
189   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
190 
191   if (GetState() != CONNECTED) {
192     HOST_LOG << "Dropping signaling message because FTL is not connected.";
193     return false;
194   }
195 
196   std::string to_error;
197   SignalingAddress to =
198       SignalingAddress::Parse(stanza.get(), SignalingAddress::TO, &to_error);
199   DCHECK(to_error.empty());
200 
201   // Synthesizing the from attribute in the message.
202   stanza->SetAttr(jingle_xmpp::QN_FROM, local_address_.id());
203 
204   std::string stanza_id = stanza->Attr(jingle_xmpp::QN_ID);
205 
206   ftl::ChromotingMessage crd_message;
207   crd_message.mutable_xmpp()->set_stanza(stanza->Str());
208   SendMessageImpl(to, crd_message,
209                   base::BindOnce(&Core::OnSendMessageResponse,
210                                  weak_factory_.GetWeakPtr(), to, stanza_id));
211 
212   // Return false if the SendMessageImpl() call above resulted in the
213   // SignalStrategy being disconnected.
214   return GetState() == CONNECTED;
215 }
216 
SendMessage(const SignalingAddress & destination_address,const ftl::ChromotingMessage & message)217 bool FtlSignalStrategy::Core::SendMessage(
218     const SignalingAddress& destination_address,
219     const ftl::ChromotingMessage& message) {
220   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
221 
222   if (GetState() != CONNECTED) {
223     HOST_LOG << "Dropping message because FTL is not connected.";
224     return false;
225   }
226 
227   SendMessageImpl(
228       destination_address, message,
229       base::BindOnce(&Core::OnSendMessageResponse, weak_factory_.GetWeakPtr(),
230                      destination_address, std::string()));
231 
232   return true;
233 }
234 
IsSignInError() const235 bool FtlSignalStrategy::Core::IsSignInError() const {
236   return is_sign_in_error_;
237 }
238 
OnGetOAuthTokenResponse(OAuthTokenGetter::Status status,const std::string & user_email,const std::string & access_token)239 void FtlSignalStrategy::Core::OnGetOAuthTokenResponse(
240     OAuthTokenGetter::Status status,
241     const std::string& user_email,
242     const std::string& access_token) {
243   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
244   if (status != OAuthTokenGetter::Status::SUCCESS) {
245     switch (status) {
246       case OAuthTokenGetter::Status::NETWORK_ERROR:
247         error_ = SignalStrategy::Error::NETWORK_ERROR;
248         break;
249       case OAuthTokenGetter::Status::AUTH_ERROR:
250         error_ = SignalStrategy::Error::AUTHENTICATION_FAILED;
251         break;
252       default:
253         NOTREACHED();
254         break;
255     }
256     is_sign_in_error_ = true;
257     Disconnect();
258     return;
259   }
260 
261   user_email_ = user_email;
262   StartReceivingMessages();
263 }
264 
OnSignInGaiaResponse(const ProtobufHttpStatus & status)265 void FtlSignalStrategy::Core::OnSignInGaiaResponse(
266     const ProtobufHttpStatus& status) {
267   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
268   if (!status.ok()) {
269     is_sign_in_error_ = true;
270     HandleProtobufHttpStatusError(FROM_HERE, status);
271     return;
272   }
273   StartReceivingMessages();
274 }
275 
StartReceivingMessages()276 void FtlSignalStrategy::Core::StartReceivingMessages() {
277   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
278   DCHECK_EQ(CONNECTING, GetState());
279   DCHECK(!messaging_client_->IsReceivingMessages());
280 
281   if (user_email_.empty()) {
282     oauth_token_getter_->CallWithToken(base::BindOnce(
283         &Core::OnGetOAuthTokenResponse, weak_factory_.GetWeakPtr()));
284     return;
285   }
286 
287   if (!registration_manager_->IsSignedIn()) {
288     registration_manager_->SignInGaia(base::BindOnce(
289         &Core::OnSignInGaiaResponse, weak_factory_.GetWeakPtr()));
290     return;
291   }
292 
293   messaging_client_->StartReceivingMessages(
294       base::BindOnce(&Core::OnReceiveMessagesStreamStarted,
295                      weak_factory_.GetWeakPtr()),
296       base::BindOnce(&Core::OnReceiveMessagesStreamClosed,
297                      weak_factory_.GetWeakPtr()));
298 }
299 
OnReceiveMessagesStreamStarted()300 void FtlSignalStrategy::Core::OnReceiveMessagesStreamStarted() {
301   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
302   local_address_ = SignalingAddress::CreateFtlSignalingAddress(
303       user_email_, registration_manager_->GetRegistrationId());
304 
305   for (auto& observer : listeners_)
306     observer.OnSignalStrategyStateChange(CONNECTED);
307 }
308 
OnReceiveMessagesStreamClosed(const ProtobufHttpStatus & status)309 void FtlSignalStrategy::Core::OnReceiveMessagesStreamClosed(
310     const ProtobufHttpStatus& status) {
311   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
312   if (status.error_code() == ProtobufHttpStatus::Code::CANCELLED) {
313     LOG(WARNING) << "ReceiveMessages stream closed with CANCELLED code.";
314   }
315   DCHECK(!status.ok());
316   HandleProtobufHttpStatusError(FROM_HERE, status);
317 }
318 
OnMessageReceived(const ftl::Id & sender_id,const std::string & sender_registration_id,const ftl::ChromotingMessage & message)319 void FtlSignalStrategy::Core::OnMessageReceived(
320     const ftl::Id& sender_id,
321     const std::string& sender_registration_id,
322     const ftl::ChromotingMessage& message) {
323   for (auto& listener : listeners_) {
324     if (listener.OnSignalStrategyIncomingMessage(
325             sender_id, sender_registration_id, message)) {
326       return;
327     }
328   }
329 
330   if (!message.has_xmpp()) {
331     LOG(WARNING) << "Ignoring message that doesn't have XMPP field.";
332     return;
333   }
334 
335   auto sender_address = SignalingAddress::CreateFtlSignalingAddress(
336       sender_id.id(), sender_registration_id);
337   DCHECK(message.xmpp().has_stanza());
338   auto stanza = base::WrapUnique<jingle_xmpp::XmlElement>(
339       jingle_xmpp::XmlElement::ForStr(message.xmpp().stanza()));
340   if (!stanza) {
341     LOG(WARNING) << "Failed to parse XMPP: " << message.xmpp().stanza();
342     return;
343   }
344   OnStanza(sender_address, std::move(stanza));
345 }
346 
SendMessageImpl(const SignalingAddress & receiver,const ftl::ChromotingMessage & message,MessagingClient::DoneCallback callback)347 void FtlSignalStrategy::Core::SendMessageImpl(
348     const SignalingAddress& receiver,
349     const ftl::ChromotingMessage& message,
350     MessagingClient::DoneCallback callback) {
351   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
352 
353   std::string receiver_username;
354   std::string receiver_registration_id;
355   bool get_info_result =
356       receiver.GetFtlInfo(&receiver_username, &receiver_registration_id);
357   if (!get_info_result) {
358     LOG(DFATAL) << "Receiver is not in FTL address: " << receiver.id();
359     return;
360   }
361 
362   std::string message_payload;
363   if (message.has_xmpp()) {
364     message_payload = message.xmpp().stanza();
365   } else if (message.has_echo()) {
366     message_payload = message.echo().message();
367   } else {
368     message_payload = "Error displaying message due to unknown format.";
369   }
370 
371   HOST_LOG << "Sending outgoing message:\n"
372            << "Receiver: " << receiver_username << "\n"
373            << "Receiver registration ID: " << receiver_registration_id << "\n"
374            << message_payload
375            << "\n=========================================================";
376 
377   messaging_client_->SendMessage(receiver_username, receiver_registration_id,
378                                  message, std::move(callback));
379 }
380 
OnSendMessageResponse(const SignalingAddress & receiver,const std::string & stanza_id,const ProtobufHttpStatus & status)381 void FtlSignalStrategy::Core::OnSendMessageResponse(
382     const SignalingAddress& receiver,
383     const std::string& stanza_id,
384     const ProtobufHttpStatus& status) {
385   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
386   if (status.ok()) {
387     return;
388   }
389 
390   if (status.error_code() == ProtobufHttpStatus::Code::UNAUTHENTICATED) {
391     HandleProtobufHttpStatusError(FROM_HERE, status);
392     return;
393   }
394 
395   LOG(ERROR) << "Failed to send message to peer. Error code: "
396              << static_cast<int>(status.error_code())
397              << ", message: " << status.error_message();
398 
399   if (stanza_id.empty()) {
400     // If the message sent was not related to signaling, then exit early.
401     return;
402   }
403 
404   // Fake an error message so JingleSession will take it as PEER_IS_OFFLINE.
405   auto error_iq = std::make_unique<jingle_xmpp::XmlElement>(jingle_xmpp::QN_IQ);
406   error_iq->SetAttr(jingle_xmpp::QN_TYPE, jingle_xmpp::STR_ERROR);
407   error_iq->SetAttr(jingle_xmpp::QN_ID, stanza_id);
408   error_iq->SetAttr(jingle_xmpp::QN_FROM, receiver.id());
409   error_iq->SetAttr(jingle_xmpp::QN_TO, local_address_.id());
410   OnStanza(receiver, std::move(error_iq));
411 }
412 
HandleProtobufHttpStatusError(const base::Location & location,const ProtobufHttpStatus & status)413 void FtlSignalStrategy::Core::HandleProtobufHttpStatusError(
414     const base::Location& location,
415     const ProtobufHttpStatus& status) {
416   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
417   DCHECK(!status.ok());
418   // We don't map HTTP_UNAUTHORIZED to AUTHENTICATION_FAILED here, as it will
419   // permanently terminate the host, which is not desirable since it might
420   // happen when the FTL registration becomes invalid while the robot account
421   // itself is still intact.
422   // AUTHENTICATION_FAILED is only reported if the OAuthTokenGetter fails to
423   // fetch the token.
424   error_ = Error::NETWORK_ERROR;
425   LOG(ERROR) << "Received server error. Error code: "
426              << static_cast<int>(status.error_code())
427              << ", message: " << status.error_message()
428              << ", location: " << location.ToString();
429   if (status.error_code() == ProtobufHttpStatus::Code::UNAUTHENTICATED ||
430       status.error_code() == ProtobufHttpStatus::Code::PERMISSION_DENIED) {
431     oauth_token_getter_->InvalidateCache();
432   }
433   Disconnect();
434 }
435 
OnStanza(const SignalingAddress & sender_address,std::unique_ptr<jingle_xmpp::XmlElement> stanza)436 void FtlSignalStrategy::Core::OnStanza(
437     const SignalingAddress& sender_address,
438     std::unique_ptr<jingle_xmpp::XmlElement> stanza) {
439   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
440 
441   // Validate the schema and FTL IDs.
442   if (stanza->Name() != jingle_xmpp::QN_IQ) {
443     LOG(DFATAL) << "Received unexpected non-IQ packet " << stanza->Str();
444     return;
445   }
446   if (SignalingAddress(stanza->Attr(jingle_xmpp::QN_FROM)) != sender_address) {
447     LOG(DFATAL) << "Expected sender: " << sender_address.id()
448                 << ", but received: " << stanza->Attr(jingle_xmpp::QN_FROM);
449     return;
450   }
451   if (SignalingAddress(stanza->Attr(jingle_xmpp::QN_TO)) != local_address_) {
452     LOG(DFATAL) << "Expected receiver: " << local_address_.id()
453                 << ", but received: " << stanza->Attr(jingle_xmpp::QN_TO);
454     return;
455   }
456 
457   HOST_LOG << "Received incoming stanza:\n"
458            << stanza->Str()
459            << "\n=========================================================";
460 
461   for (auto& listener : listeners_) {
462     if (listener.OnSignalStrategyIncomingStanza(stanza.get()))
463       return;
464   }
465 }
466 
FtlSignalStrategy(std::unique_ptr<OAuthTokenGetter> oauth_token_getter,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,std::unique_ptr<FtlDeviceIdProvider> device_id_provider,SignalingTracker * signaling_tracker)467 FtlSignalStrategy::FtlSignalStrategy(
468     std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
469     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
470     std::unique_ptr<FtlDeviceIdProvider> device_id_provider,
471     SignalingTracker* signaling_tracker) {
472   // TODO(yuweih): Just make FtlMessagingClient own FtlRegistrationManager and
473   // call SignInGaia() transparently.
474   auto registration_manager = std::make_unique<FtlRegistrationManager>(
475       oauth_token_getter.get(), url_loader_factory,
476       std::move(device_id_provider));
477   auto messaging_client = std::make_unique<FtlMessagingClient>(
478       oauth_token_getter.get(), url_loader_factory, registration_manager.get(),
479       signaling_tracker);
480   CreateCore(std::move(oauth_token_getter), std::move(registration_manager),
481              std::move(messaging_client));
482 }
483 
FtlSignalStrategy(std::unique_ptr<OAuthTokenGetter> oauth_token_getter,std::unique_ptr<RegistrationManager> registration_manager,std::unique_ptr<MessagingClient> messaging_client)484 FtlSignalStrategy::FtlSignalStrategy(
485     std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
486     std::unique_ptr<RegistrationManager> registration_manager,
487     std::unique_ptr<MessagingClient> messaging_client) {
488   CreateCore(std::move(oauth_token_getter), std::move(registration_manager),
489              std::move(messaging_client));
490 }
491 
~FtlSignalStrategy()492 FtlSignalStrategy::~FtlSignalStrategy() {
493   // All listeners should be removed at this point, so it's safe to detach
494   // |core_|.
495   base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
496                                                      core_.release());
497 }
498 
Connect()499 void FtlSignalStrategy::Connect() {
500   core_->Connect();
501 }
502 
Disconnect()503 void FtlSignalStrategy::Disconnect() {
504   core_->Disconnect();
505 }
506 
GetState() const507 SignalStrategy::State FtlSignalStrategy::GetState() const {
508   return core_->GetState();
509 }
510 
GetError() const511 SignalStrategy::Error FtlSignalStrategy::GetError() const {
512   return core_->GetError();
513 }
514 
GetLocalAddress() const515 const SignalingAddress& FtlSignalStrategy::GetLocalAddress() const {
516   return core_->GetLocalAddress();
517 }
518 
AddListener(Listener * listener)519 void FtlSignalStrategy::AddListener(Listener* listener) {
520   core_->AddListener(listener);
521 }
522 
RemoveListener(Listener * listener)523 void FtlSignalStrategy::RemoveListener(Listener* listener) {
524   core_->RemoveListener(listener);
525 }
526 
SendStanza(std::unique_ptr<jingle_xmpp::XmlElement> stanza)527 bool FtlSignalStrategy::SendStanza(
528     std::unique_ptr<jingle_xmpp::XmlElement> stanza) {
529   return core_->SendStanza(std::move(stanza));
530 }
531 
SendMessage(const SignalingAddress & destination_address,const ftl::ChromotingMessage & message)532 bool FtlSignalStrategy::SendMessage(const SignalingAddress& destination_address,
533                                     const ftl::ChromotingMessage& message) {
534   return core_->SendMessage(destination_address, message);
535 }
536 
GetNextId()537 std::string FtlSignalStrategy::GetNextId() {
538   return base::NumberToString(base::RandUint64());
539 }
540 
IsSignInError() const541 bool FtlSignalStrategy::IsSignInError() const {
542   return core_->IsSignInError();
543 }
544 
CreateCore(std::unique_ptr<OAuthTokenGetter> oauth_token_getter,std::unique_ptr<RegistrationManager> registration_manager,std::unique_ptr<MessagingClient> messaging_client)545 void FtlSignalStrategy::CreateCore(
546     std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
547     std::unique_ptr<RegistrationManager> registration_manager,
548     std::unique_ptr<MessagingClient> messaging_client) {
549   core_ = std::make_unique<Core>(std::move(oauth_token_getter),
550                                  std::move(registration_manager),
551                                  std::move(messaging_client));
552 }
553 
554 }  // namespace remoting
555