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