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