1 // Copyright 2013 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.
5 #include "google_apis/gcm/gcm_client_impl.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/sequenced_task_runner.h"
13 #include "google_apis/gcm/base/mcs_message.h"
14 #include "google_apis/gcm/base/mcs_util.h"
15 #include "google_apis/gcm/engine/checkin_request.h"
16 #include "google_apis/gcm/engine/connection_factory_impl.h"
17 #include "google_apis/gcm/engine/gcm_store_impl.h"
18 #include "google_apis/gcm/engine/mcs_client.h"
19 #include "google_apis/gcm/engine/user_list.h"
20 #include "google_apis/gcm/protocol/mcs.pb.h"
21 #include "net/http/http_network_session.h"
27 const char kMCSEndpoint[] = "https://mtalk.google.com:5228";
30 GCMClientImpl::GCMClientImpl()
31 : state_(UNINITIALIZED),
32 url_request_context_getter_(NULL),
33 pending_checkins_deleter_(&pending_checkins_) {
36 GCMClientImpl::~GCMClientImpl() {
39 void GCMClientImpl::Initialize(
40 const checkin_proto::ChromeBuildProto& chrome_build_proto,
41 const base::FilePath& path,
42 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
43 const scoped_refptr<net::URLRequestContextGetter>&
44 url_request_context_getter) {
45 DCHECK_EQ(UNINITIALIZED, state_);
46 DCHECK(url_request_context_getter);
48 chrome_build_proto_.CopyFrom(chrome_build_proto);
49 url_request_context_getter_ = url_request_context_getter;
51 gcm_store_.reset(new GCMStoreImpl(false, path, blocking_task_runner));
52 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
53 base::Unretained(this)));
54 user_list_.reset(new UserList(gcm_store_.get()));
55 connection_factory_.reset(new ConnectionFactoryImpl(GURL(kMCSEndpoint),
58 mcs_client_.reset(new MCSClient(&clock_,
59 connection_factory_.get(),
64 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
65 DCHECK_EQ(LOADING, state_);
67 if (!result->success) {
72 user_list_->Initialize(result->serial_number_mappings);
74 device_checkin_info_.android_id = result->device_android_id;
75 device_checkin_info_.secret = result->device_security_token;
76 InitializeMCSClient(result.Pass());
77 if (!device_checkin_info_.IsValid()) {
78 device_checkin_info_.Reset();
79 state_ = INITIAL_DEVICE_CHECKIN;
80 StartCheckin(0, device_checkin_info_);
87 void GCMClientImpl::InitializeMCSClient(
88 scoped_ptr<GCMStore::LoadResult> result) {
89 mcs_client_->Initialize(
90 base::Bind(&GCMClientImpl::OnMCSError, base::Unretained(this)),
91 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
92 base::Unretained(this)),
93 base::Bind(&GCMClientImpl::OnMessageSentToMCS, base::Unretained(this)),
97 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
98 const CheckinInfo& checkin_info) {
99 DCHECK(!device_checkin_info_.IsValid());
102 device_checkin_info_.android_id = checkin_info.android_id;
103 device_checkin_info_.secret = checkin_info.secret;
104 gcm_store_->SetDeviceCredentials(
105 checkin_info.android_id, checkin_info.secret,
106 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
107 base::Unretained(this)));
111 void GCMClientImpl::StartMCSLogin() {
112 DCHECK_EQ(READY, state_);
113 DCHECK(device_checkin_info_.IsValid());
114 mcs_client_->Login(device_checkin_info_.android_id,
115 device_checkin_info_.secret);
118 void GCMClientImpl::ResetState() {
119 state_ = UNINITIALIZED;
120 // TODO(fgorski): reset all of the necessart objects and start over.
123 void GCMClientImpl::StartCheckin(int64 user_serial_number,
124 const CheckinInfo& checkin_info) {
125 DCHECK_EQ(0U, pending_checkins_.count(user_serial_number));
126 CheckinRequest* checkin_request =
128 base::Bind(&GCMClientImpl::OnCheckinCompleted,
129 base::Unretained(this),
133 checkin_info.android_id,
135 url_request_context_getter_);
136 pending_checkins_[user_serial_number] = checkin_request;
137 checkin_request->Start();
140 void GCMClientImpl::OnCheckinCompleted(int64 user_serial_number,
142 uint64 security_token) {
143 CheckinInfo checkin_info;
144 checkin_info.android_id = android_id;
145 checkin_info.secret = security_token;
147 // Delete the checkin request.
148 PendingCheckins::iterator iter = pending_checkins_.find(user_serial_number);
149 DCHECK(iter != pending_checkins_.end());
151 pending_checkins_.erase(iter);
153 if (user_serial_number == 0) {
154 OnDeviceCheckinCompleted(checkin_info);
158 Delegate* delegate = user_list_->GetDelegateBySerialNumber(
160 // TODO(fgorski): Add a reasonable Result here. It is possible that we are
161 // missing the right parameter on the CheckinRequest level.
162 delegate->OnCheckInFinished(checkin_info, SUCCESS);
165 void GCMClientImpl::OnDeviceCheckinCompleted(const CheckinInfo& checkin_info) {
166 if (!checkin_info.IsValid()) {
167 // TODO(fgorski): Trigger a retry logic here. (no need to start over).
171 if (state_ == INITIAL_DEVICE_CHECKIN) {
172 OnFirstTimeDeviceCheckinCompleted(checkin_info);
174 DCHECK_EQ(READY, state_);
175 if (device_checkin_info_.android_id != checkin_info.android_id ||
176 device_checkin_info_.secret != checkin_info.secret) {
179 // TODO(fgorski): Reset the checkin timer.
184 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
185 // TODO(fgorski): This is one of the signals that store needs a rebuild.
189 void GCMClientImpl::SetUserDelegate(const std::string& username,
190 Delegate* delegate) {
191 DCHECK(!username.empty());
193 user_list_->SetDelegate(
196 base::Bind(&GCMClientImpl::SetDelegateCompleted, base::Unretained(this)));
199 void GCMClientImpl::SetDelegateCompleted(const std::string& username,
200 int64 user_serial_number) {
201 Delegate* delegate = user_list_->GetDelegateByUsername(username);
203 if (state_ == READY) {
204 delegate->OnLoadingCompleted();
209 void GCMClientImpl::CheckIn(const std::string& username) {
212 void GCMClientImpl::Register(const std::string& username,
213 const std::string& app_id,
214 const std::string& cert,
215 const std::vector<std::string>& sender_ids) {
218 void GCMClientImpl::Unregister(const std::string& username,
219 const std::string& app_id) {
222 void GCMClientImpl::Send(const std::string& username,
223 const std::string& app_id,
224 const std::string& receiver_id,
225 const OutgoingMessage& message) {
228 bool GCMClientImpl::IsLoading() const {
229 return state_ != READY;
232 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
233 // We need to do the message parsing here and then dispatch it to the right
234 // delegate related to that message
235 switch (message.tag()) {
236 case kLoginResponseTag:
237 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
239 case kDataMessageStanzaTag:
240 DVLOG(1) << "A downstream message received. Processing...";
241 HandleIncomingMessage(message);
244 NOTREACHED() << "Message with unexpected tag received by GCMClient";
249 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
250 const std::string& app_id,
251 const std::string& message_id,
252 MCSClient::MessageSendStatus status) {
253 // TODO(fgorski): This is only a placeholder, it likely has to change the
254 // arguments to be able to identify the user and app.
257 void GCMClientImpl::OnMCSError() {
258 // TODO(fgorski): For now it replaces the initialization method. Long term it
259 // should have an error or status passed in.
262 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
263 const mcs_proto::DataMessageStanza& data_message_stanza =
264 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
265 message.GetProtobuf());
266 IncomingMessage incoming_message;
267 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
268 incoming_message.data[data_message_stanza.app_data(i).key()] =
269 data_message_stanza.app_data(i).value();
272 int64 user_serial_number = data_message_stanza.device_user_id();
274 user_list_->GetDelegateBySerialNumber(user_serial_number);
276 DVLOG(1) << "Found delegate for serial number: " << user_serial_number;
277 base::MessageLoop::current()->PostTask(
279 base::Bind(&GCMClientImpl::NotifyDelegateOnMessageReceived,
280 base::Unretained(this),
282 data_message_stanza.category(),
285 DVLOG(1) << "Delegate for serial number: " << user_serial_number
290 void GCMClientImpl::NotifyDelegateOnMessageReceived(
291 GCMClient::Delegate* delegate,
292 const std::string& app_id,
293 const IncomingMessage& incoming_message) {
294 delegate->OnMessageReceived(app_id, incoming_message);