OLD | NEW |
---|---|
(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 | |
OLD | NEW |