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

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: Updating the failing test 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 = 60 * 60; // 1 day in seconds.
21 const int kGCMUpdateIntervalHours = 24;
22 const int kGCMUpdateEarlyStartHours = 6;
jianli 2014/08/28 18:43:51 Please comment on this.
fgorski 2014/08/29 02:29:17 Done.
23 const char kRegistrationIdMessgaeKey[] = "id";
24 const char kTokenMessageKey[] = "t";
25 const char kAccountMessageKey[] = "a";
26 const char kRemoveAccountMessageKey[] = "r";
27 const char kRemoveAccountMessageValue[] = "1";
28
29 std::string GenerateMessageID() {
30 return base::GenerateGUID();
31 }
32
33 } // namespace
34
35 GCMAccountMapper::GCMAccountMapper(GCMDriver* gcm_driver)
36 : gcm_driver_(gcm_driver),
37 clock_(new base::DefaultClock),
38 initialized_(false),
39 weak_ptr_factory_(this) {
40 }
41
42 GCMAccountMapper::~GCMAccountMapper() {
43 }
44
45 void GCMAccountMapper::Initialize(
46 const std::vector<AccountMapping>& account_mappings,
47 const std::string& registration_id) {
48 DCHECK(!initialized_);
49 initialized_ = true;
50 registration_id_ = registration_id;
51
52 accounts_ = account_mappings;
53
54 gcm_driver_->AddAppHandler(kGCMAccountMapperAppId, this);
55
56 // TODO(fgorski): if no registration ID, get registration ID.
57 }
58
59 void GCMAccountMapper::SetAccountTokens(
60 const std::vector<GCMClient::AccountTokenInfo> account_tokens) {
61 DCHECK(initialized_);
62
63 // Start from removing the old tokens, from all of the known accounts.
64 for (AccountMappings::iterator iter = accounts_.begin();
65 iter != accounts_.end();
66 ++iter) {
67 iter->access_token.clear();
68 // Sending a message triggers a status change, this is a check if a message
69 // expired and a relevant event was missed.
jianli 2014/08/28 18:43:51 What do you mean by "relevant event was missed"?
fgorski 2014/08/29 02:29:17 Done.
70 if ((iter->status == AccountMapping::ADDING ||
71 iter->status == AccountMapping::REMOVING) &&
72 IsLastStatusChangeOlderThanTTL(iter->status_change_timestamp)) {
73 if (iter->status == AccountMapping::ADDING)
74 iter->status = AccountMapping::NEW;
75 iter->last_message_type = AccountMapping::MSG_NONE;
76 iter->last_message_id.clear();
77 }
78 }
79
80 // Update the internal collection of mappings with the new tokens.
81 for (std::vector<GCMClient::AccountTokenInfo>::const_iterator token_iter =
82 account_tokens.begin();
83 token_iter != account_tokens.end();
84 ++token_iter) {
85 AccountMapping* account_mapping =
86 FindMappingByAccountId(token_iter->account_id);
87 if (!account_mapping) {
88 AccountMapping new_mapping;
89 new_mapping.status = AccountMapping::NEW;
90 new_mapping.account_id = token_iter->account_id;
91 new_mapping.access_token = token_iter->access_token;
92 new_mapping.email = token_iter->email;
93 new_mapping.last_message_type = AccountMapping::MSG_NONE;
94 accounts_.push_back(new_mapping);
95 } else {
96 // Since we got a token for an account, drop the remove message and treat
97 // it as mapped.
98 if (account_mapping->last_message_type == AccountMapping::MSG_REMOVE) {
99 account_mapping->status = AccountMapping::MAPPED;
100 account_mapping->status_change_timestamp = base::Time();
101 account_mapping->last_message_type = AccountMapping::MSG_NONE;
102 account_mapping->last_message_id.clear();
103 }
104
105 account_mapping->email = token_iter->email;
106 account_mapping->access_token = token_iter->access_token;
107 }
108 }
109
110 // Decide what to do with each account (either start mapping, or start
111 // removing).
112 for (AccountMappings::iterator mappings_iter = accounts_.begin();
113 mappings_iter != accounts_.end();
114 ++mappings_iter) {
115 if (mappings_iter->access_token.empty()) {
116 if (mappings_iter->last_message_id.empty() ||
jianli 2014/08/28 18:43:50 Please add comment saying something like: // No
fgorski 2014/08/29 02:29:17 Done.
117 mappings_iter->last_message_type != AccountMapping::MSG_REMOVE) {
118 SendRemoveMappingMessage(*mappings_iter);
119 }
120 } else {
121 // A message is sent for all of the mappings considered NEW, or for those
122 // mapped accounts that are due for update.
123 if (mappings_iter->status == AccountMapping::NEW ||
124 (mappings_iter->status == AccountMapping::MAPPED &&
125 IsUpdateDue(mappings_iter->status_change_timestamp))) {
126 SendAddMappingMessage(*mappings_iter);
127 }
128 }
129 }
130 }
131
132 void GCMAccountMapper::ShutdownHandler() {
133 gcm_driver_->RemoveAppHandler(kGCMAccountMapperAppId);
134 }
135
136 void GCMAccountMapper::OnMessage(const std::string& app_id,
137 const GCMClient::IncomingMessage& message) {
138 // Account message does not expect messages right now.
139 }
140
141 void GCMAccountMapper::OnMessagesDeleted(const std::string& app_id) {
142 // Account message does not expect messages right now.
143 }
144
145 void GCMAccountMapper::OnSendError(
146 const std::string& app_id,
147 const GCMClient::SendErrorDetails& send_error_details) {
148 DCHECK_EQ(app_id, kGCMAccountMapperAppId);
149
150 AccountMappings::iterator account_mapping_it =
151 FindMappingByMessageId(send_error_details.message_id);
152
153 if (account_mapping_it == accounts_.end())
154 return;
155
156 // TODO(fgorski): What other status can we get here if any? What should we do
157 // about that. (likely TTL_EXCEEDED is the only one)
158 DCHECK_EQ(send_error_details.result, GCMClient::TTL_EXCEEDED);
159
160 if (account_mapping_it->last_message_type == AccountMapping::MSG_REMOVE) {
161 SendRemoveMappingMessage(*account_mapping_it);
jianli 2014/08/28 18:43:51 Do you mean keeping resending remove message when
fgorski 2014/08/29 02:29:17 Done.
162 } else {
163 DCHECK_EQ(account_mapping_it->last_message_type, AccountMapping::MSG_ADD);
164 if (account_mapping_it->status == AccountMapping::ADDING) {
165 // There is no mapping established, so we can remove the entry.
166 // Getting a fresh token will trigger a new attempt.
167 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
168 accounts_.erase(account_mapping_it);
169 } else {
170 // Account is already MAPPED, we have to wait for another token.
171 account_mapping_it->last_message_id.clear();
172 account_mapping_it->last_message_type = AccountMapping::MSG_NONE;
173 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
174 }
175 }
176 }
177
178 void GCMAccountMapper::OnSendAcknowledged(const std::string& app_id,
179 const std::string& message_id) {
180 DCHECK_EQ(app_id, kGCMAccountMapperAppId);
181 AccountMappings::iterator account_mapping_it =
182 FindMappingByMessageId(message_id);
183
184 DVLOG(1) << "OnSendAcknowledged with message ID: " << message_id;
185
186 if (account_mapping_it == accounts_.end())
187 return;
188
189 // Here is where we advance a status of a mapping and persist or remove.
190 if (account_mapping_it->last_message_type == AccountMapping::MSG_REMOVE) {
191 DCHECK_EQ(account_mapping_it->status, AccountMapping::REMOVING);
jianli 2014/08/28 18:43:51 It seems to me that the mapping status could be fo
fgorski 2014/08/29 02:29:17 REMOVED is removed. I am seeing if I can remove th
192 // Message removing the account has been confirmed by the GCM, we can remove
193 // all the information related to the account (from memory and store).
194 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
195 accounts_.erase(account_mapping_it);
196 } else {
197 DCHECK_EQ(account_mapping_it->last_message_type, AccountMapping::MSG_ADD);
198 // Mapping status is ADDING only when it is a first time mapping.
199 DCHECK(account_mapping_it->status == AccountMapping::ADDING ||
200 account_mapping_it->status == AccountMapping::MAPPED);
201
202 // Account is marked as mapped with the current time.
203 account_mapping_it->status = AccountMapping::MAPPED;
204 account_mapping_it->status_change_timestamp = clock_->Now();
205 // There is no pending message for the account.
206 account_mapping_it->last_message_type = AccountMapping::MSG_NONE;
207 account_mapping_it->last_message_id.clear();
208
209 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
210 }
211 }
212
213 void GCMAccountMapper::OnConnected(const net::IPEndPoint& ip_endpoint) {
214 // Account mapper ignores connection status.
215 }
216
217 void GCMAccountMapper::OnDisconnected() {
218 // Account mapper ignores connection status.
219 }
220
221 bool GCMAccountMapper::CanHandle(const std::string& app_id) const {
222 return app_id.compare(kGCMAccountMapperAppId) == 0;
223 }
224
225 void GCMAccountMapper::SendAddMappingMessage(AccountMapping& account_mapping) {
226 GCMClient::OutgoingMessage outgoing_message;
227 outgoing_message.id = GenerateMessageID();
228 outgoing_message.time_to_live = kGCMAccountMapperMessageTTL;
229 outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
230 outgoing_message.data[kTokenMessageKey] = account_mapping.access_token;
231 outgoing_message.data[kAccountMessageKey] = account_mapping.email;
232
233 gcm_driver_->Send(kGCMAccountMapperAppId,
234 kGCMAccountMapperSenderId,
235 outgoing_message,
236 base::Bind(&GCMAccountMapper::OnSendFinished,
237 weak_ptr_factory_.GetWeakPtr(),
238 account_mapping.account_id,
239 AccountMapping::MSG_ADD));
240 }
241
242 void GCMAccountMapper::SendRemoveMappingMessage(
243 AccountMapping& account_mapping) {
244 // We want to persist an account that is being removed as quickly as possible
245 // as well as clean up the last message information.
246 if (account_mapping.status != AccountMapping::REMOVING) {
247 account_mapping.status = AccountMapping::REMOVING;
248 account_mapping.status_change_timestamp = clock_->Now();
249 }
250
251 account_mapping.last_message_id.clear();
252 account_mapping.last_message_type = AccountMapping::MSG_NONE;
253
254 gcm_driver_->UpdateAccountMapping(account_mapping);
255
256 GCMClient::OutgoingMessage outgoing_message;
257 outgoing_message.id = GenerateMessageID();
258 outgoing_message.time_to_live = kGCMAccountMapperMessageTTL;
259 outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
260 outgoing_message.data[kAccountMessageKey] = account_mapping.email;
261 outgoing_message.data[kRemoveAccountMessageKey] = kRemoveAccountMessageValue;
262
263 gcm_driver_->Send(kGCMAccountMapperAppId,
264 kGCMAccountMapperSenderId,
265 outgoing_message,
266 base::Bind(&GCMAccountMapper::OnSendFinished,
267 weak_ptr_factory_.GetWeakPtr(),
268 account_mapping.account_id,
269 AccountMapping::MSG_REMOVE));
270 }
271
272 void GCMAccountMapper::OnSendFinished(const std::string& account_id,
273 AccountMapping::MessageType message_type,
274 const std::string& message_id,
275 GCMClient::Result result) {
276 // TODO(fgorski): Add another attempt, in case the QUEUE is not full.
jianli 2014/08/28 18:43:51 remove "not"?
fgorski 2014/08/29 02:29:17 I actually mean it. If the queue is full, no need
277 if (result != GCMClient::SUCCESS)
278 return;
279
280 AccountMapping* account_mapping_it = FindMappingByAccountId(account_id);
jianli 2014/08/28 18:43:51 Remove "_it" since it is not an iterator.
fgorski 2014/08/29 02:29:17 Done.
281 DCHECK(account_mapping_it);
282
283 // If we are dealing with account with status NEW, it is the first time
284 // mapping, and we should mark it as ADDING.
285 if (account_mapping_it->status == AccountMapping::NEW) {
286 DCHECK(message_type == AccountMapping::MSG_ADD);
287 account_mapping_it->status = AccountMapping::ADDING;
288 account_mapping_it->status_change_timestamp = clock_->Now();
289 }
290
291 account_mapping_it->last_message_type = message_type;
292 account_mapping_it->last_message_id = message_id;
293
294 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
295 }
296
297 bool GCMAccountMapper::IsUpdateDue(const base::Time& last_update_time) const {
298 return last_update_time +
299 base::TimeDelta::FromHours(kGCMUpdateIntervalHours -
300 kGCMUpdateEarlyStartHours) <
301 clock_->Now();
302 }
303
304 bool GCMAccountMapper::IsLastStatusChangeOlderThanTTL(
305 const base::Time& estimated_send_time) const {
306 return estimated_send_time +
307 base::TimeDelta::FromSeconds(kGCMAccountMapperMessageTTL) <
308 clock_->Now();
309 }
310
311 AccountMapping* GCMAccountMapper::FindMappingByAccountId(
312 const std::string& account_id) {
313 for (AccountMappings::iterator iter = accounts_.begin();
314 iter != accounts_.end();
315 ++iter) {
316 if (iter->account_id == account_id)
317 return &*iter;
318 }
319
320 return NULL;
321 }
322
323 GCMAccountMapper::AccountMappings::iterator
324 GCMAccountMapper::FindMappingByMessageId(const std::string& message_id) {
325 for (std::vector<AccountMapping>::iterator iter = accounts_.begin();
326 iter != accounts_.end();
327 ++iter) {
328 if (iter->last_message_id == message_id)
329 return iter;
330 }
331
332 return accounts_.end();
333 }
334
335 void GCMAccountMapper::SetClockForTesting(scoped_ptr<base::Clock> clock) {
336 clock_ = clock.Pass();
337 }
338
339 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698