Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: components/gcm_driver/gcm_account_mapper.cc

Issue 491443004: [GCM] Adding GCMAccountMapper to link signed in profile to accounts. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Adding more tests, fixing discovered bugs. Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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 "components/gcm_driver/gcm_account_mapper.h"
6
7 #include "base/bind.h"
8 #include "base/guid.h"
9 #include "base/time/clock.h"
10 #include "base/time/default_clock.h"
11 #include "components/gcm_driver/gcm_driver_desktop.h"
12 #include "google_apis/gcm/engine/gcm_store.h"
13
14 namespace gcm {
15
16 namespace {
17
18 const char kGCMAccountMapperSenderId[] = "745476177629";
19 const char kGCMAccountMapperAppId[] = "com.google.android.gms";
20 const int kGCMAccountMapperMessageTTL = 24 * 60 * 60; // 1 day in seconds.
21 const int kGCMUpdateInterval = 24; // hours.
jianli 2014/08/27 18:12:00 Add the Hour suffix to the constant name.
fgorski 2014/08/27 21:47:17 Done.
22 const char kRegistrationIdMessgaeKey[] = "id";
23 const char kTokenMessageKey[] = "t";
24 const char kAccountMessageKey[] = "a";
25 const char kRemoveAccountMessageKey[] = "r";
26 const char kRemoveAccountMessageValue[] = "1";
27
28 std::string GenerateMessageID() {
29 return base::GenerateGUID();
30 }
31
32 } // namespace
33
34 GCMAccountMapper::GCMAccountMapper(GCMDriver* gcm_driver)
35 : gcm_driver_(gcm_driver),
36 clock_(new base::DefaultClock),
37 initialized_(false),
38 weak_ptr_factory_(this) {
39 }
40
41 GCMAccountMapper::~GCMAccountMapper() {
42 }
43
44 void GCMAccountMapper::Initialize(
45 const std::vector<AccountMapping>& account_mappings,
46 const std::string& registration_id) {
47 initialized_ = true;
jianli 2014/08/27 18:12:00 nit: add DCHECK for !initialized_.
fgorski 2014/08/27 21:47:17 Done.
48 registration_id_ = registration_id;
49
50 accounts_.insert(
51 accounts_.end(), account_mappings.begin(), account_mappings.end());
jianli 2014/08/27 18:12:01 nit: simpler to say accounts_ = account_mappings
fgorski 2014/08/27 21:47:16 Done.
52
53 gcm_driver_->AddAppHandler(kGCMAccountMapperAppId, this);
54
55 // TODO(fgorski): if no registration ID, get registration ID.
56 }
57
58 void GCMAccountMapper::SetAccountTokens(
59 const std::vector<GCMClient::AccountTokenInfo> account_tokens) {
60 DCHECK(initialized_);
61
62 // Start from removing the old tokens, from all of the known accounts.
63 for (AccountMappings::iterator iter = accounts_.begin();
64 iter != accounts_.end();
65 ++iter) {
66 iter->access_token.clear();
67 // Sending a message triggers a status change, so we can change if the
68 // message is perhaps expired (and event was missed).
jianli 2014/08/27 18:12:00 is perhaps expired => has expired
fgorski 2014/08/27 21:47:16 Done.
69 if (IsMessageExpired(iter->status_change_timestamp)) {
jianli 2014/08/27 18:12:00 I found out that it was hard to understand using a
fgorski 2014/08/27 21:47:17 I didn't want to store too much. It is not necessa
70 iter->last_message_type = AccountMapping::MSG_NONE;
71 iter->last_message_id.clear();
72 }
73 }
74
75 // Update the internal collection of mappings with the new tokens.
76 for (std::vector<GCMClient::AccountTokenInfo>::const_iterator token_iter =
77 account_tokens.begin();
78 token_iter != account_tokens.end();
79 ++token_iter) {
80 AccountMapping* account_mapping =
81 FindMappingByAccountId(token_iter->account_id);
82 if (!account_mapping) {
83 AccountMapping new_mapping;
84 new_mapping.status = AccountMapping::NEW;
85 new_mapping.account_id = token_iter->account_id;
86 new_mapping.access_token = token_iter->access_token;
87 new_mapping.email = token_iter->email;
88 new_mapping.last_message_type = AccountMapping::MSG_NONE;
89 accounts_.push_back(new_mapping);
90 } else {
91 // Since we got a token for an account, drop the remove message and treat
92 // it as new.
jianli 2014/08/27 18:12:00 When we get a new token for an account, do we need
fgorski 2014/08/27 21:47:16 Getting form AccountMapping::REMOVING to AccountMa
93 if (account_mapping->last_message_type == AccountMapping::MSG_REMOVE) {
94 account_mapping->status = AccountMapping::MAPPED;
95 account_mapping->status_change_timestamp = base::Time();
96 account_mapping->last_message_type = AccountMapping::MSG_NONE;
97 account_mapping->last_message_id.clear();
98 }
99
100 account_mapping->email = token_iter->email;
101 account_mapping->access_token = token_iter->access_token;
102 }
103 }
104
105 // Decide what to do with each account (either start mapping, or start
106 // removing).
107 for (AccountMappings::iterator mappings_iter = accounts_.begin();
108 mappings_iter != accounts_.end();
109 ++mappings_iter) {
110 if (mappings_iter->access_token.empty()) {
111 if (mappings_iter->last_message_id.empty() ||
112 mappings_iter->last_message_type != AccountMapping::MSG_REMOVE) {
113 SendRemoveMappingMessage(*mappings_iter);
114 }
115 } else {
116 if (IsUpdateDue(mappings_iter->status_change_timestamp) ||
jianli 2014/08/27 18:12:00 Should || be &&, per our discussion? Please also c
fgorski 2014/08/27 21:47:17 Done. I've added account status checks to make it
117 mappings_iter->status == AccountMapping::NEW) {
118 SendAddMappingMessage(*mappings_iter);
119 }
120 }
121 }
122 }
123
124 void GCMAccountMapper::ShutdownHandler() {
125 gcm_driver_->RemoveAppHandler(kGCMAccountMapperAppId);
126 }
127
128 void GCMAccountMapper::OnMessage(const std::string& app_id,
129 const GCMClient::IncomingMessage& message) {
130 // Account message does not expect messages right now.
131 }
132
133 void GCMAccountMapper::OnMessagesDeleted(const std::string& app_id) {
134 // Account message does not expect messages right now.
135 }
136
137 void GCMAccountMapper::OnSendError(
138 const std::string& app_id,
139 const GCMClient::SendErrorDetails& send_error_details) {
140 DCHECK_EQ(app_id, kGCMAccountMapperAppId);
141
142 AccountMappings::iterator account_mapping_it =
143 FindMappingByMessageId(send_error_details.message_id);
144
145 if (account_mapping_it == accounts_.end())
146 return;
147
148 // TODO(fgorski): What other status can we get here if any? What should we do
149 // about that. (likely TTL_EXCEEDED is the only one)
150 DCHECK_EQ(send_error_details.result, GCMClient::TTL_EXCEEDED);
151
152 if (account_mapping_it->last_message_type == AccountMapping::MSG_REMOVE) {
153 SendRemoveMappingMessage(*account_mapping_it);
154 } else {
155 DCHECK_EQ(account_mapping_it->last_message_type, AccountMapping::MSG_ADD);
156 if (account_mapping_it->status == AccountMapping::ADDING) {
157 // There is no mapping established, so we can remove the entry.
158 // Getting a fresh token will trigger a new attempt.
159 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
160 accounts_.erase(account_mapping_it);
161 } else {
162 // Account is already MAPPED, we have to wait for another token.
163 account_mapping_it->last_message_id.clear();
164 account_mapping_it->last_message_type = AccountMapping::MSG_NONE;
165 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
166 }
167 }
168 }
169
170 void GCMAccountMapper::OnSendAcknowledged(const std::string& app_id,
171 const std::string& message_id) {
172 DCHECK_EQ(app_id, kGCMAccountMapperAppId);
173 AccountMappings::iterator account_mapping_it =
174 FindMappingByMessageId(message_id);
175
176 DVLOG(1) << "OnSendAcknowledged with message ID: " << message_id;
177
178 if (account_mapping_it == accounts_.end())
179 return;
180
181 // here is where we advance a status of a mapping and persist or remove.
jianli 2014/08/27 18:12:00 nit: make 1st letter capitalized
fgorski 2014/08/27 21:47:17 Done.
182 if (account_mapping_it->last_message_type == AccountMapping::MSG_REMOVE) {
183 DVLOG(1) << "Removal message found, account_id "
184 << account_mapping_it->account_id;
185 // Message removing the account has been confirmed by the GCM, we can remove
186 // all the information related to the account (from memory and store).
187 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
188 accounts_.erase(account_mapping_it);
189 } else {
190 DVLOG(1) << "Add message found, account_id "
191 << account_mapping_it->account_id;
192 // Message adding the account has been confirmed by the GCM, we can now
jianli 2014/08/27 18:12:00 The comment is a bit hard to follow since it goes
fgorski 2014/08/27 21:47:17 Done. Restructured, rephrased and added a DCHECK.
193 // ensure the status and status change timestamp is updated, while message
194 // information is cleared.
195 DCHECK_EQ(account_mapping_it->last_message_type, AccountMapping::MSG_ADD);
196 if (account_mapping_it->status == AccountMapping::ADDING)
197 account_mapping_it->status = AccountMapping::MAPPED;
198
199 account_mapping_it->status_change_timestamp = clock_->Now();
200 account_mapping_it->last_message_type = AccountMapping::MSG_NONE;
201 account_mapping_it->last_message_id.clear();
202
203 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
204 }
205 }
206
207 void GCMAccountMapper::OnConnected(const net::IPEndPoint& ip_endpoint) {
208 // Account mapper ignores connection status.
209 }
210
211 void GCMAccountMapper::OnDisconnected() {
212 // Account mapper ignores connection status.
213 }
214
215 bool GCMAccountMapper::CanHandle(const std::string& app_id) const {
216 return app_id.compare(kGCMAccountMapperAppId) == 0;
217 }
218
219 void GCMAccountMapper::SendAddMappingMessage(AccountMapping& account_mapping) {
220 // Account mapping is only written to the store once a message was
221 // successfully sent.
222 if (account_mapping.status == AccountMapping::NEW)
223 account_mapping.status = AccountMapping::ADDING;
jianli 2014/08/27 18:12:00 Why do we not update account_mapping.status_change
fgorski 2014/08/27 21:47:17 This is by design. I want the state of ADDING only
224
225 GCMClient::OutgoingMessage outgoing_message;
226 outgoing_message.id = GenerateMessageID();
227 outgoing_message.time_to_live = kGCMAccountMapperMessageTTL;
228 outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
229 outgoing_message.data[kTokenMessageKey] = account_mapping.access_token;
230 outgoing_message.data[kAccountMessageKey] = account_mapping.email;
231
232 gcm_driver_->Send(kGCMAccountMapperAppId,
233 kGCMAccountMapperSenderId,
234 outgoing_message,
235 base::Bind(&GCMAccountMapper::OnSendFinished,
236 weak_ptr_factory_.GetWeakPtr(),
237 account_mapping.account_id,
238 AccountMapping::MSG_ADD));
239 }
240
241 void GCMAccountMapper::SendRemoveMappingMessage(
242 AccountMapping& account_mapping) {
243 if (account_mapping.status != AccountMapping::REMOVING) {
244 account_mapping.status = AccountMapping::REMOVING;
245 account_mapping.status_change_timestamp = clock_->Now();
246 }
247
248 account_mapping.last_message_id.clear();
249 account_mapping.last_message_type = AccountMapping::MSG_NONE;
250 gcm_driver_->UpdateAccountMapping(account_mapping);
251
252 GCMClient::OutgoingMessage outgoing_message;
253 outgoing_message.id = GenerateMessageID();
254 outgoing_message.time_to_live = kGCMAccountMapperMessageTTL;
255 outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
256 outgoing_message.data[kAccountMessageKey] = account_mapping.email;
257 outgoing_message.data[kRemoveAccountMessageKey] = kRemoveAccountMessageValue;
258
259 gcm_driver_->Send(kGCMAccountMapperAppId,
260 kGCMAccountMapperSenderId,
261 outgoing_message,
262 base::Bind(&GCMAccountMapper::OnSendFinished,
263 weak_ptr_factory_.GetWeakPtr(),
264 account_mapping.account_id,
265 AccountMapping::MSG_REMOVE));
266 }
267
268 void GCMAccountMapper::OnSendFinished(const std::string& account_id,
269 AccountMapping::MessageType message_type,
270 const std::string& message_id,
271 GCMClient::Result result) {
272 if (result == GCMClient::SUCCESS) {
jianli 2014/08/27 18:12:01 What is expected when result is not SUCCESS? If w
fgorski 2014/08/27 21:47:17 Done.
273 AccountMapping* account_mapping_it = FindMappingByAccountId(account_id);
274 DCHECK(account_mapping_it);
275
276 account_mapping_it->last_message_type = message_type;
277 account_mapping_it->last_message_id = message_id;
278
279 // Update of the status when removing the account happens before a message
jianli 2014/08/27 18:12:00 Not sure I understand "removing the account happen
fgorski 2014/08/27 21:47:17 Done together with the work on the comment above.
280 // is sent.
281 if (message_type == AccountMapping::MSG_ADD)
282 account_mapping_it->status_change_timestamp = clock_->Now();
283
284 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
285 }
286 }
287
288 bool GCMAccountMapper::IsUpdateDue(const base::Time& last_update_time) {
289 return last_update_time + base::TimeDelta::FromHours(kGCMUpdateInterval) <
jianli 2014/08/27 18:12:00 Should "<" be ">="?
fgorski 2014/08/27 21:47:17 < is ok. the question is, should I update now or
290 clock_->Now();
291 }
292
293 bool GCMAccountMapper::IsMessageExpired(const base::Time& estimated_send_time) {
294 return estimated_send_time +
295 base::TimeDelta::FromSeconds(kGCMAccountMapperMessageTTL) <
296 clock_->Now();
297 }
298
299 AccountMapping* GCMAccountMapper::FindMappingByAccountId(
300 const std::string& account_id) {
301 for (AccountMappings::iterator iter = accounts_.begin();
302 iter != accounts_.end();
303 ++iter) {
304 if (iter->account_id == account_id)
305 return &*iter;
306 }
307
308 return NULL;
309 }
310
311 GCMAccountMapper::AccountMappings::iterator
312 GCMAccountMapper::FindMappingByMessageId(const std::string& message_id) {
313 for (std::vector<AccountMapping>::iterator iter = accounts_.begin();
314 iter != accounts_.end();
315 ++iter) {
316 if (iter->last_message_id == message_id)
317 return iter;
318 }
319
320 return accounts_.end();
321 }
322
323 void GCMAccountMapper::SetClockForTesting(scoped_ptr<base::Clock> clock) {
324 clock_ = clock.Pass();
325 }
326
327 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698