6ab61afb8e6c364cdf435d25fbece331c8577864
[platform/framework/web/crosswalk.git] / src / google_apis / gcm / gcm_client_impl.cc
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.
4
5 #include "google_apis/gcm/gcm_client_impl.h"
6
7 #include "base/bind.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"
22 #include "url/gurl.h"
23
24 namespace gcm {
25
26 namespace {
27 const char kMCSEndpoint[] = "https://mtalk.google.com:5228";
28 }  // namespace
29
30 GCMClientImpl::GCMClientImpl()
31     : state_(UNINITIALIZED),
32       url_request_context_getter_(NULL),
33       pending_checkins_deleter_(&pending_checkins_) {
34 }
35
36 GCMClientImpl::~GCMClientImpl() {
37 }
38
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);
47
48   chrome_build_proto_.CopyFrom(chrome_build_proto);
49   url_request_context_getter_ = url_request_context_getter;
50
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),
56                                                       network_session_,
57                                                       net_log_.net_log()));
58   mcs_client_.reset(new MCSClient(&clock_,
59                                   connection_factory_.get(),
60                                   gcm_store_.get()));
61   state_ = LOADING;
62 }
63
64 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
65   DCHECK_EQ(LOADING, state_);
66
67   if (!result->success) {
68     ResetState();
69     return;
70   }
71
72   user_list_->Initialize(result->serial_number_mappings);
73
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_);
81   } else {
82     state_ = READY;
83     StartMCSLogin();
84   }
85 }
86
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)),
94       result.Pass());
95 }
96
97 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
98     const CheckinInfo& checkin_info) {
99   DCHECK(!device_checkin_info_.IsValid());
100
101   state_ = READY;
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)));
108   StartMCSLogin();
109 }
110
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);
116 }
117
118 void GCMClientImpl::ResetState() {
119   state_ = UNINITIALIZED;
120   // TODO(fgorski): reset all of the necessart objects and start over.
121 }
122
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 =
127       new CheckinRequest(
128           base::Bind(&GCMClientImpl::OnCheckinCompleted,
129                      base::Unretained(this),
130                      user_serial_number),
131           chrome_build_proto_,
132           user_serial_number,
133           checkin_info.android_id,
134           checkin_info.secret,
135           url_request_context_getter_);
136   pending_checkins_[user_serial_number] = checkin_request;
137   checkin_request->Start();
138 }
139
140 void GCMClientImpl::OnCheckinCompleted(int64 user_serial_number,
141                                        uint64 android_id,
142                                        uint64 security_token) {
143   CheckinInfo checkin_info;
144   checkin_info.android_id = android_id;
145   checkin_info.secret = security_token;
146
147   // Delete the checkin request.
148   PendingCheckins::iterator iter = pending_checkins_.find(user_serial_number);
149   DCHECK(iter != pending_checkins_.end());
150   delete iter->second;
151   pending_checkins_.erase(iter);
152
153   if (user_serial_number == 0) {
154     OnDeviceCheckinCompleted(checkin_info);
155     return;
156   }
157
158   Delegate* delegate = user_list_->GetDelegateBySerialNumber(
159                                        user_serial_number);
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);
163 }
164
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).
168     return;
169   }
170
171   if (state_ == INITIAL_DEVICE_CHECKIN) {
172     OnFirstTimeDeviceCheckinCompleted(checkin_info);
173   } else {
174     DCHECK_EQ(READY, state_);
175     if (device_checkin_info_.android_id != checkin_info.android_id ||
176         device_checkin_info_.secret != checkin_info.secret) {
177       ResetState();
178     } else {
179       // TODO(fgorski): Reset the checkin timer.
180     }
181   }
182 }
183
184 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
185   // TODO(fgorski): This is one of the signals that store needs a rebuild.
186   DCHECK(success);
187 }
188
189 void GCMClientImpl::SetUserDelegate(const std::string& username,
190                                     Delegate* delegate) {
191   DCHECK(!username.empty());
192   DCHECK(delegate);
193   user_list_->SetDelegate(
194       username,
195       delegate,
196       base::Bind(&GCMClientImpl::SetDelegateCompleted, base::Unretained(this)));
197 }
198
199 void GCMClientImpl::SetDelegateCompleted(const std::string& username,
200                                          int64 user_serial_number) {
201   Delegate* delegate = user_list_->GetDelegateByUsername(username);
202   DCHECK(delegate);
203   if (state_ == READY) {
204     delegate->OnLoadingCompleted();
205     return;
206   }
207 }
208
209 void GCMClientImpl::CheckIn(const std::string& username) {
210 }
211
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) {
216 }
217
218 void GCMClientImpl::Unregister(const std::string& username,
219                                const std::string& app_id) {
220 }
221
222 void GCMClientImpl::Send(const std::string& username,
223                          const std::string& app_id,
224                          const std::string& receiver_id,
225                          const OutgoingMessage& message) {
226 }
227
228 bool GCMClientImpl::IsLoading() const {
229   return state_ != READY;
230 }
231
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.";
238       return;
239     case kDataMessageStanzaTag:
240       DVLOG(1) << "A downstream message received. Processing...";
241       HandleIncomingMessage(message);
242       return;
243     default:
244       NOTREACHED() << "Message with unexpected tag received by GCMClient";
245       return;
246   }
247 }
248
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.
255 }
256
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.
260 }
261
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();
270   }
271
272   int64 user_serial_number = data_message_stanza.device_user_id();
273   Delegate* delegate =
274       user_list_->GetDelegateBySerialNumber(user_serial_number);
275   if (delegate) {
276     DVLOG(1) << "Found delegate for serial number: " << user_serial_number;
277     base::MessageLoop::current()->PostTask(
278         FROM_HERE,
279         base::Bind(&GCMClientImpl::NotifyDelegateOnMessageReceived,
280                    base::Unretained(this),
281                    delegate,
282                    data_message_stanza.category(),
283                    incoming_message));
284   } else {
285     DVLOG(1) << "Delegate for serial number: " << user_serial_number
286              << " not found.";
287   }
288 }
289
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);
295 }
296
297 }  // namespace gcm