Chromium Code Reviews| 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 |