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

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: Removing message type from the AccountMapping 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 kGCMAddMappingMessageTTL = 30 * 60; // 0.5 hours in seconds.
21 const int kGCMRemoveMappingMessageTTL = 24 * 60 * 60; // 1 day in seconds.
22 const int kGCMUpdateIntervalHours = 24;
23 // Because adding an account mapping is dependent on a fresh OAuth2 token, we
jianli 2014/09/02 18:00:10 is dependent => depends
fgorski 2014/09/03 21:37:02 Done.
24 // allow the update to happen earlier then update interval, if it is within
jianli 2014/09/02 18:00:09 then update interval => than update due time
fgorski 2014/09/03 21:37:02 Done.
25 // the early start time to take advantage of that token.
26 const int kGCMUpdateEarlyStartHours = 6;
27 const char kRegistrationIdMessgaeKey[] = "id";
28 const char kTokenMessageKey[] = "t";
29 const char kAccountMessageKey[] = "a";
30 const char kRemoveAccountMessageKey[] = "r";
31 const char kRemoveAccountMessageValue[] = "1";
32
33 std::string GenerateMessageID() {
34 return base::GenerateGUID();
35 }
36
37 } // namespace
38
39 GCMAccountMapper::GCMAccountMapper(GCMDriver* gcm_driver)
40 : gcm_driver_(gcm_driver),
41 clock_(new base::DefaultClock),
42 initialized_(false),
43 weak_ptr_factory_(this) {
44 }
45
46 GCMAccountMapper::~GCMAccountMapper() {
47 }
48
49 void GCMAccountMapper::Initialize(
50 const std::vector<AccountMapping>& account_mappings,
51 const std::string& registration_id) {
52 DCHECK(!initialized_);
53 initialized_ = true;
54 registration_id_ = registration_id;
55
56 accounts_ = account_mappings;
57
58 gcm_driver_->AddAppHandler(kGCMAccountMapperAppId, this);
59
60 // TODO(fgorski): if no registration ID, get registration ID.
61 }
62
63 void GCMAccountMapper::SetAccountTokens(
64 const std::vector<GCMClient::AccountTokenInfo> account_tokens) {
65 DCHECK(initialized_);
66
67 // Start from removing the old tokens, from all of the known accounts.
68 for (AccountMappings::iterator iter = accounts_.begin();
69 iter != accounts_.end();
70 ++iter) {
71 iter->access_token.clear();
72 }
73
74 // Update the internal collection of mappings with the new tokens.
75 for (std::vector<GCMClient::AccountTokenInfo>::const_iterator token_iter =
76 account_tokens.begin();
77 token_iter != account_tokens.end();
78 ++token_iter) {
79 AccountMapping* account_mapping =
80 FindMappingByAccountId(token_iter->account_id);
81 if (!account_mapping) {
82 AccountMapping new_mapping;
83 new_mapping.status = AccountMapping::NEW;
84 new_mapping.account_id = token_iter->account_id;
85 new_mapping.access_token = token_iter->access_token;
86 new_mapping.email = token_iter->email;
87 accounts_.push_back(new_mapping);
88 } else {
89 // Since we got a token for an account, drop the remove message and treat
90 // it as mapped.
91 if (account_mapping->status == AccountMapping::REMOVING) {
92 account_mapping->status = AccountMapping::MAPPED;
93 account_mapping->status_change_timestamp = base::Time();
94 account_mapping->last_message_id.clear();
95 }
96
97 account_mapping->email = token_iter->email;
98 account_mapping->access_token = token_iter->access_token;
99 }
100 }
101
102 // Decide what to do with each account (either start mapping, or start
103 // removing).
104 for (AccountMappings::iterator mappings_iter = accounts_.begin();
105 mappings_iter != accounts_.end();
106 ++mappings_iter) {
107 if (mappings_iter->access_token.empty()) {
108 // Send a remove message if the account was not previously removing,
jianli 2014/09/02 18:00:10 removing => being removed
fgorski 2014/09/03 21:37:02 Done.
109 // or it doesn't have a pending message, or the pending message is
110 // already expired, but OnSendError event was lost.
111 if (mappings_iter->status != AccountMapping::REMOVING ||
112 mappings_iter->last_message_id.empty() ||
113 IsLastStatusChangeOlderThanTTL(*mappings_iter)) {
114 SendRemoveMappingMessage(*mappings_iter);
115 }
116 } else {
117 // A message is sent for all of the mappings considered NEW, or mappings
118 // that are ADDING, but have expired message (OnSendError event lost), or
119 // for those mapped accounts that can be refreshed.
120 if (mappings_iter->status == AccountMapping::NEW ||
121 (mappings_iter->status == AccountMapping::ADDING &&
122 IsLastStatusChangeOlderThanTTL(*mappings_iter)) ||
123 (mappings_iter->status == AccountMapping::MAPPED &&
124 CanTriggerUpdate(mappings_iter->status_change_timestamp))) {
125 mappings_iter->last_message_id.clear();
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->status == AccountMapping::REMOVING) {
jianli 2014/09/02 18:00:10 It would be better to add the check for TTL_EXCEED
fgorski 2014/09/03 21:37:02 Done.
161 // Another message to remove mapping can be sent immediately, because TTL
162 // for those is one day. No need to back off.
163 SendRemoveMappingMessage(*account_mapping_it);
164 } else {
165 if (account_mapping_it->status == AccountMapping::ADDING) {
166 // There is no mapping established, so we can remove the entry.
167 // Getting a fresh token will trigger a new attempt.
168 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
169 accounts_.erase(account_mapping_it);
170 } else {
171 // Account is already MAPPED, we have to wait for another token.
172 account_mapping_it->last_message_id.clear();
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->status == AccountMapping::REMOVING) {
191 // Message removing the account has been confirmed by the GCM, we can remove
192 // all the information related to the account (from memory and store).
193 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
194 accounts_.erase(account_mapping_it);
195 } else {
196 // Mapping status is ADDING only when it is a first time mapping.
197 DCHECK(account_mapping_it->status == AccountMapping::ADDING ||
198 account_mapping_it->status == AccountMapping::MAPPED);
199
200 // Account is marked as mapped with the current time.
201 account_mapping_it->status = AccountMapping::MAPPED;
202 account_mapping_it->status_change_timestamp = clock_->Now();
203 // There is no pending message for the account.
204 account_mapping_it->last_message_id.clear();
205
206 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
207 }
208 }
209
210 bool GCMAccountMapper::CanHandle(const std::string& app_id) const {
211 return app_id.compare(kGCMAccountMapperAppId) == 0;
212 }
213
214 void GCMAccountMapper::SendAddMappingMessage(AccountMapping& account_mapping) {
215 GCMClient::OutgoingMessage outgoing_message;
216 outgoing_message.id = GenerateMessageID();
217 outgoing_message.time_to_live = kGCMAddMappingMessageTTL;
218 outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
219 outgoing_message.data[kTokenMessageKey] = account_mapping.access_token;
220 outgoing_message.data[kAccountMessageKey] = account_mapping.email;
221
222 gcm_driver_->Send(kGCMAccountMapperAppId,
223 kGCMAccountMapperSenderId,
224 outgoing_message,
225 base::Bind(&GCMAccountMapper::OnSendFinished,
226 weak_ptr_factory_.GetWeakPtr(),
227 account_mapping.account_id));
228 }
229
230 void GCMAccountMapper::SendRemoveMappingMessage(
231 AccountMapping& account_mapping) {
232 // We want to persist an account that is being removed as quickly as possible
233 // as well as clean up the last message information.
234 if (account_mapping.status != AccountMapping::REMOVING) {
235 account_mapping.status = AccountMapping::REMOVING;
236 account_mapping.status_change_timestamp = clock_->Now();
237 }
238
239 account_mapping.last_message_id.clear();
240
241 gcm_driver_->UpdateAccountMapping(account_mapping);
242
243 GCMClient::OutgoingMessage outgoing_message;
244 outgoing_message.id = GenerateMessageID();
245 outgoing_message.time_to_live = kGCMRemoveMappingMessageTTL;
246 outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
247 outgoing_message.data[kAccountMessageKey] = account_mapping.email;
248 outgoing_message.data[kRemoveAccountMessageKey] = kRemoveAccountMessageValue;
jianli 2014/09/02 18:00:10 The code in line 241-254 is very similar to the co
fgorski 2014/09/03 21:37:02 Done.
249
250 gcm_driver_->Send(kGCMAccountMapperAppId,
251 kGCMAccountMapperSenderId,
252 outgoing_message,
253 base::Bind(&GCMAccountMapper::OnSendFinished,
254 weak_ptr_factory_.GetWeakPtr(),
255 account_mapping.account_id));
256 }
257
258 void GCMAccountMapper::OnSendFinished(const std::string& account_id,
259 const std::string& message_id,
260 GCMClient::Result result) {
261 // TODO(fgorski): Add another attempt, in case the QUEUE is not full.
262 if (result != GCMClient::SUCCESS)
263 return;
264
265 AccountMapping* account_mapping = FindMappingByAccountId(account_id);
266 DCHECK(account_mapping);
267
268 // If we are dealing with account with status NEW, it is the first time
269 // mapping, and we should mark it as ADDING.
270 if (account_mapping->status == AccountMapping::NEW) {
271 account_mapping->status = AccountMapping::ADDING;
272 account_mapping->status_change_timestamp = clock_->Now();
273 }
274
275 account_mapping->last_message_id = message_id;
276
277 gcm_driver_->UpdateAccountMapping(*account_mapping);
278 }
279
280 bool GCMAccountMapper::CanTriggerUpdate(
281 const base::Time& last_update_time) const {
282 return last_update_time +
283 base::TimeDelta::FromHours(kGCMUpdateIntervalHours -
284 kGCMUpdateEarlyStartHours) <
285 clock_->Now();
286 }
287
288 bool GCMAccountMapper::IsLastStatusChangeOlderThanTTL(
289 const AccountMapping& account_mapping) const {
290 base::TimeDelta time_to_live;
jianli 2014/09/02 18:00:10 Probably more efficient to say: int ttl_seconds
fgorski 2014/09/03 21:37:02 Done.
291 if (account_mapping.status == AccountMapping::REMOVING)
292 time_to_live = base::TimeDelta::FromSeconds(kGCMRemoveMappingMessageTTL);
293 else
294 time_to_live = base::TimeDelta::FromSeconds(kGCMAddMappingMessageTTL);
295 return account_mapping.status_change_timestamp + time_to_live < clock_->Now();
296 }
297
298 AccountMapping* GCMAccountMapper::FindMappingByAccountId(
299 const std::string& account_id) {
300 for (AccountMappings::iterator iter = accounts_.begin();
301 iter != accounts_.end();
302 ++iter) {
303 if (iter->account_id == account_id)
304 return &*iter;
305 }
306
307 return NULL;
308 }
309
310 GCMAccountMapper::AccountMappings::iterator
311 GCMAccountMapper::FindMappingByMessageId(const std::string& message_id) {
312 for (std::vector<AccountMapping>::iterator iter = accounts_.begin();
313 iter != accounts_.end();
314 ++iter) {
315 if (iter->last_message_id == message_id)
316 return iter;
317 }
318
319 return accounts_.end();
320 }
321
322 void GCMAccountMapper::SetClockForTesting(scoped_ptr<base::Clock> clock) {
323 clock_ = clock.Pass();
324 }
325
326 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698