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: Addressing feedback from Jian Li 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 dependents on a fresh OAuth2 token, we
24 // allow the update to happen earlier than update due time, if it is within
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 kRemoveAccountKey[] = "r";
31 const char kRemoveAccountValue[] = "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 being removed,
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 if (send_error_details.result != GCMClient::TTL_EXCEEDED) {
157 DVLOG(1) << "Send error result different than TTL EXCEEDED: "
158 << send_error_details.result << ". "
159 << "Postponing the retry until a new batch of tokens arrives.";
160 return;
161 }
162
163 if (account_mapping_it->status == AccountMapping::REMOVING) {
164 // Another message to remove mapping can be sent immediately, because TTL
165 // for those is one day. No need to back off.
166 SendRemoveMappingMessage(*account_mapping_it);
167 } else {
168 if (account_mapping_it->status == AccountMapping::ADDING) {
169 // There is no mapping established, so we can remove the entry.
170 // Getting a fresh token will trigger a new attempt.
171 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
172 accounts_.erase(account_mapping_it);
173 } else {
174 // Account is already MAPPED, we have to wait for another token.
175 account_mapping_it->last_message_id.clear();
176 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
177 }
178 }
179 }
180
181 void GCMAccountMapper::OnSendAcknowledged(const std::string& app_id,
182 const std::string& message_id) {
183 DCHECK_EQ(app_id, kGCMAccountMapperAppId);
184 AccountMappings::iterator account_mapping_it =
185 FindMappingByMessageId(message_id);
186
187 DVLOG(1) << "OnSendAcknowledged with message ID: " << message_id;
188
189 if (account_mapping_it == accounts_.end())
190 return;
191
192 // Here is where we advance a status of a mapping and persist or remove.
193 if (account_mapping_it->status == AccountMapping::REMOVING) {
194 // Message removing the account has been confirmed by the GCM, we can remove
195 // all the information related to the account (from memory and store).
196 gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
197 accounts_.erase(account_mapping_it);
198 } else {
199 // Mapping status is ADDING only when it is a first time mapping.
200 DCHECK(account_mapping_it->status == AccountMapping::ADDING ||
201 account_mapping_it->status == AccountMapping::MAPPED);
202
203 // Account is marked as mapped with the current time.
204 account_mapping_it->status = AccountMapping::MAPPED;
205 account_mapping_it->status_change_timestamp = clock_->Now();
206 // There is no pending message for the account.
207 account_mapping_it->last_message_id.clear();
208
209 gcm_driver_->UpdateAccountMapping(*account_mapping_it);
210 }
211 }
212
213 bool GCMAccountMapper::CanHandle(const std::string& app_id) const {
214 return app_id.compare(kGCMAccountMapperAppId) == 0;
215 }
216
217 void GCMAccountMapper::SendAddMappingMessage(AccountMapping& account_mapping) {
218 CreateAndSendMessage(account_mapping);
219 }
220
221 void GCMAccountMapper::SendRemoveMappingMessage(
222 AccountMapping& account_mapping) {
223 // We want to persist an account that is being removed as quickly as possible
224 // as well as clean up the last message information.
225 if (account_mapping.status != AccountMapping::REMOVING) {
226 account_mapping.status = AccountMapping::REMOVING;
227 account_mapping.status_change_timestamp = clock_->Now();
228 }
229
230 account_mapping.last_message_id.clear();
231
232 gcm_driver_->UpdateAccountMapping(account_mapping);
233
234 CreateAndSendMessage(account_mapping);
235 }
236
237 void GCMAccountMapper::CreateAndSendMessage(
238 const AccountMapping& account_mapping) {
239 GCMClient::OutgoingMessage outgoing_message;
240 outgoing_message.id = GenerateMessageID();
241 outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
242 outgoing_message.data[kAccountMessageKey] = account_mapping.email;
243
244 if (account_mapping.status == AccountMapping::REMOVING) {
245 outgoing_message.time_to_live = kGCMRemoveMappingMessageTTL;
246 outgoing_message.data[kRemoveAccountKey] = kRemoveAccountValue;
247 } else {
248 outgoing_message.data[kTokenMessageKey] = account_mapping.access_token;
249 outgoing_message.time_to_live = kGCMAddMappingMessageTTL;
250 }
251
252 gcm_driver_->Send(kGCMAccountMapperAppId,
253 kGCMAccountMapperSenderId,
254 outgoing_message,
255 base::Bind(&GCMAccountMapper::OnSendFinished,
256 weak_ptr_factory_.GetWeakPtr(),
257 account_mapping.account_id));
258 }
259
260
261 void GCMAccountMapper::OnSendFinished(const std::string& account_id,
262 const std::string& message_id,
263 GCMClient::Result result) {
264 // TODO(fgorski): Add another attempt, in case the QUEUE is not full.
265 if (result != GCMClient::SUCCESS)
266 return;
267
268 AccountMapping* account_mapping = FindMappingByAccountId(account_id);
269 DCHECK(account_mapping);
270
271 // If we are dealing with account with status NEW, it is the first time
272 // mapping, and we should mark it as ADDING.
273 if (account_mapping->status == AccountMapping::NEW) {
274 account_mapping->status = AccountMapping::ADDING;
275 account_mapping->status_change_timestamp = clock_->Now();
276 }
277
278 account_mapping->last_message_id = message_id;
279
280 gcm_driver_->UpdateAccountMapping(*account_mapping);
281 }
282
283 bool GCMAccountMapper::CanTriggerUpdate(
284 const base::Time& last_update_time) const {
285 return last_update_time +
286 base::TimeDelta::FromHours(kGCMUpdateIntervalHours -
287 kGCMUpdateEarlyStartHours) <
288 clock_->Now();
289 }
290
291 bool GCMAccountMapper::IsLastStatusChangeOlderThanTTL(
292 const AccountMapping& account_mapping) const {
293 int ttl_seconds = account_mapping.status == AccountMapping::REMOVING ?
294 kGCMRemoveMappingMessageTTL : kGCMAddMappingMessageTTL;
295 return account_mapping.status_change_timestamp +
296 base::TimeDelta::FromSeconds(ttl_seconds) < 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
« no previous file with comments | « components/gcm_driver/gcm_account_mapper.h ('k') | components/gcm_driver/gcm_account_mapper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698