11320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Copyright 2014 The Chromium Authors. All rights reserved.
21320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Use of this source code is governed by a BSD-style license that can be
31320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// found in the LICENSE file.
41320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "components/gcm_driver/gcm_account_mapper.h"
61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/bind.h"
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/guid.h"
91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/time/clock.h"
101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/time/default_clock.h"
111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "components/gcm_driver/gcm_driver_desktop.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "google_apis/gcm/engine/gcm_store.h"
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace gcm {
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace {
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kGCMAccountMapperSenderId[] = "745476177629";
191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kGCMAccountMapperAppId[] = "com.google.android.gms";
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kGCMAddMappingMessageTTL = 30 * 60;  // 0.5 hours in seconds.
211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kGCMRemoveMappingMessageTTL = 24 * 60 * 60;  // 1 day in seconds.
221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kGCMUpdateIntervalHours = 24;
231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Because adding an account mapping dependents on a fresh OAuth2 token, we
241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// allow the update to happen earlier than update due time, if it is within
251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// the early start time to take advantage of that token.
261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kGCMUpdateEarlyStartHours = 6;
271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kRegistrationIdMessgaeKey[] = "id";
281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kTokenMessageKey[] = "t";
291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kAccountMessageKey[] = "a";
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kRemoveAccountKey[] = "r";
311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kRemoveAccountValue[] = "1";
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistd::string GenerateMessageID() {
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return base::GenerateGUID();
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}  // namespace
381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciGCMAccountMapper::GCMAccountMapper(GCMDriver* gcm_driver)
401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    : gcm_driver_(gcm_driver),
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      clock_(new base::DefaultClock),
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      initialized_(false),
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      weak_ptr_factory_(this) {
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciGCMAccountMapper::~GCMAccountMapper() {
471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::Initialize(
501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const std::vector<AccountMapping>& account_mappings) {
511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(!initialized_);
521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  initialized_ = true;
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  accounts_ = account_mappings;
541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gcm_driver_->AddAppHandler(kGCMAccountMapperAppId, this);
551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  GetRegistration();
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::SetAccountTokens(
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const std::vector<GCMClient::AccountTokenInfo> account_tokens) {
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // If account mapper is not ready to handle tasks yet, save the latest
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // account tokens and return.
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!IsReady()) {
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    pending_account_tokens_ = account_tokens;
641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // If mapper is initialized, but still does not have registration ID,
651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // maybe the registration gave up. Retrying in case.
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (initialized_)
671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      GetRegistration();
681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Start from removing the old tokens, from all of the known accounts.
721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for (AccountMappings::iterator iter = accounts_.begin();
731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       iter != accounts_.end();
741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       ++iter) {
751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    iter->access_token.clear();
761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Update the internal collection of mappings with the new tokens.
791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for (std::vector<GCMClient::AccountTokenInfo>::const_iterator token_iter =
801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci           account_tokens.begin();
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       token_iter != account_tokens.end();
821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       ++token_iter) {
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    AccountMapping* account_mapping =
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        FindMappingByAccountId(token_iter->account_id);
851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!account_mapping) {
861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      AccountMapping new_mapping;
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      new_mapping.status = AccountMapping::NEW;
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      new_mapping.account_id = token_iter->account_id;
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      new_mapping.access_token = token_iter->access_token;
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      new_mapping.email = token_iter->email;
911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      accounts_.push_back(new_mapping);
921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    } else {
931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // Since we got a token for an account, drop the remove message and treat
941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // it as mapped.
951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (account_mapping->status == AccountMapping::REMOVING) {
961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        account_mapping->status = AccountMapping::MAPPED;
971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        account_mapping->status_change_timestamp = base::Time();
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        account_mapping->last_message_id.clear();
991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      }
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      account_mapping->email = token_iter->email;
1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      account_mapping->access_token = token_iter->access_token;
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Decide what to do with each account (either start mapping, or start
1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // removing).
1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for (AccountMappings::iterator mappings_iter = accounts_.begin();
1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       mappings_iter != accounts_.end();
1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       ++mappings_iter) {
1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (mappings_iter->access_token.empty()) {
1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // Send a remove message if the account was not previously being removed,
1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // or it doesn't have a pending message, or the pending message is
1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // already expired, but OnSendError event was lost.
1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (mappings_iter->status != AccountMapping::REMOVING ||
1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          mappings_iter->last_message_id.empty() ||
1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          IsLastStatusChangeOlderThanTTL(*mappings_iter)) {
1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        SendRemoveMappingMessage(*mappings_iter);
1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      }
1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    } else {
1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // A message is sent for all of the mappings considered NEW, or mappings
1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // that are ADDING, but have expired message (OnSendError event lost), or
1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // for those mapped accounts that can be refreshed.
1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (mappings_iter->status == AccountMapping::NEW ||
1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          (mappings_iter->status == AccountMapping::ADDING &&
1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci           IsLastStatusChangeOlderThanTTL(*mappings_iter)) ||
1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          (mappings_iter->status == AccountMapping::MAPPED &&
1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci           CanTriggerUpdate(mappings_iter->status_change_timestamp))) {
1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mappings_iter->last_message_id.clear();
1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        SendAddMappingMessage(*mappings_iter);
1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      }
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::ShutdownHandler() {
1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gcm_driver_->RemoveAppHandler(kGCMAccountMapperAppId);
1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::OnMessage(const std::string& app_id,
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                 const GCMClient::IncomingMessage& message) {
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Account message does not expect messages right now.
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::OnMessagesDeleted(const std::string& app_id) {
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Account message does not expect messages right now.
1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::OnSendError(
1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const std::string& app_id,
1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const GCMClient::SendErrorDetails& send_error_details) {
1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK_EQ(app_id, kGCMAccountMapperAppId);
1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  AccountMappings::iterator account_mapping_it =
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      FindMappingByMessageId(send_error_details.message_id);
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (account_mapping_it == accounts_.end())
1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (send_error_details.result != GCMClient::TTL_EXCEEDED) {
1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    DVLOG(1) << "Send error result different than TTL EXCEEDED: "
1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci             << send_error_details.result << ". "
1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci             << "Postponing the retry until a new batch of tokens arrives.";
1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (account_mapping_it->status == AccountMapping::REMOVING) {
1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Another message to remove mapping can be sent immediately, because TTL
1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // for those is one day. No need to back off.
1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    SendRemoveMappingMessage(*account_mapping_it);
1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else {
1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (account_mapping_it->status == AccountMapping::ADDING) {
1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // There is no mapping established, so we can remove the entry.
1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // Getting a fresh token will trigger a new attempt.
1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      accounts_.erase(account_mapping_it);
1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    } else {
1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // Account is already MAPPED, we have to wait for another token.
1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      account_mapping_it->last_message_id.clear();
1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      gcm_driver_->UpdateAccountMapping(*account_mapping_it);
1811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::OnSendAcknowledged(const std::string& app_id,
1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                          const std::string& message_id) {
1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK_EQ(app_id, kGCMAccountMapperAppId);
1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  AccountMappings::iterator account_mapping_it =
1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      FindMappingByMessageId(message_id);
1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DVLOG(1) << "OnSendAcknowledged with message ID: " << message_id;
1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (account_mapping_it == accounts_.end())
1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Here is where we advance a status of a mapping and persist or remove.
1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (account_mapping_it->status == AccountMapping::REMOVING) {
1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Message removing the account has been confirmed by the GCM, we can remove
1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // all the information related to the account (from memory and store).
2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    gcm_driver_->RemoveAccountMapping(account_mapping_it->account_id);
2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    accounts_.erase(account_mapping_it);
2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else {
2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Mapping status is ADDING only when it is a first time mapping.
2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    DCHECK(account_mapping_it->status == AccountMapping::ADDING ||
2051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci           account_mapping_it->status == AccountMapping::MAPPED);
2061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Account is marked as mapped with the current time.
2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    account_mapping_it->status = AccountMapping::MAPPED;
2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    account_mapping_it->status_change_timestamp = clock_->Now();
2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // There is no pending message for the account.
2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    account_mapping_it->last_message_id.clear();
2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    gcm_driver_->UpdateAccountMapping(*account_mapping_it);
2141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool GCMAccountMapper::CanHandle(const std::string& app_id) const {
2181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return app_id.compare(kGCMAccountMapperAppId) == 0;
2191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool GCMAccountMapper::IsReady() {
2221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return initialized_ && !registration_id_.empty();
2231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::SendAddMappingMessage(AccountMapping& account_mapping) {
2261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  CreateAndSendMessage(account_mapping);
2271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::SendRemoveMappingMessage(
2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    AccountMapping& account_mapping) {
2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // We want to persist an account that is being removed as quickly as possible
2321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // as well as clean up the last message information.
2331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (account_mapping.status != AccountMapping::REMOVING) {
2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    account_mapping.status = AccountMapping::REMOVING;
2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    account_mapping.status_change_timestamp = clock_->Now();
2361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  account_mapping.last_message_id.clear();
2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gcm_driver_->UpdateAccountMapping(account_mapping);
2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  CreateAndSendMessage(account_mapping);
2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::CreateAndSendMessage(
2461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const AccountMapping& account_mapping) {
2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  GCMClient::OutgoingMessage outgoing_message;
2481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  outgoing_message.id = GenerateMessageID();
2491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  outgoing_message.data[kRegistrationIdMessgaeKey] = registration_id_;
2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  outgoing_message.data[kAccountMessageKey] = account_mapping.email;
2511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (account_mapping.status == AccountMapping::REMOVING) {
2531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    outgoing_message.time_to_live = kGCMRemoveMappingMessageTTL;
2541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    outgoing_message.data[kRemoveAccountKey] = kRemoveAccountValue;
2551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else {
2561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    outgoing_message.data[kTokenMessageKey] = account_mapping.access_token;
2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    outgoing_message.time_to_live = kGCMAddMappingMessageTTL;
2581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
2591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gcm_driver_->Send(kGCMAccountMapperAppId,
2611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    kGCMAccountMapperSenderId,
2621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    outgoing_message,
2631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    base::Bind(&GCMAccountMapper::OnSendFinished,
2641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                               weak_ptr_factory_.GetWeakPtr(),
2651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                               account_mapping.account_id));
2661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::OnSendFinished(const std::string& account_id,
2691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                      const std::string& message_id,
2701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                      GCMClient::Result result) {
2711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // TODO(fgorski): Add another attempt, in case the QUEUE is not full.
2721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (result != GCMClient::SUCCESS)
2731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
2741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  AccountMapping* account_mapping = FindMappingByAccountId(account_id);
2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(account_mapping);
2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // If we are dealing with account with status NEW, it is the first time
2791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // mapping, and we should mark it as ADDING.
2801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (account_mapping->status == AccountMapping::NEW) {
2811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    account_mapping->status = AccountMapping::ADDING;
2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    account_mapping->status_change_timestamp = clock_->Now();
2831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
2841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  account_mapping->last_message_id = message_id;
2861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gcm_driver_->UpdateAccountMapping(*account_mapping);
2881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::GetRegistration() {
2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(registration_id_.empty());
2921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  std::vector<std::string> sender_ids;
2931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  sender_ids.push_back(kGCMAccountMapperSenderId);
2941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gcm_driver_->Register(kGCMAccountMapperAppId,
2951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        sender_ids,
2961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        base::Bind(&GCMAccountMapper::OnRegisterFinished,
2971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                   weak_ptr_factory_.GetWeakPtr()));
2981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::OnRegisterFinished(const std::string& registration_id,
3011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                          GCMClient::Result result) {
3021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (result == GCMClient::SUCCESS)
3031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    registration_id_ = registration_id;
3041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (IsReady()) {
3061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!pending_account_tokens_.empty()) {
3071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      SetAccountTokens(pending_account_tokens_);
3081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      pending_account_tokens_.clear();
3091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
3101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
3111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool GCMAccountMapper::CanTriggerUpdate(
3141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const base::Time& last_update_time) const {
3151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return last_update_time +
3161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci             base::TimeDelta::FromHours(kGCMUpdateIntervalHours -
3171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                        kGCMUpdateEarlyStartHours) <
3181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci         clock_->Now();
3191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool GCMAccountMapper::IsLastStatusChangeOlderThanTTL(
3221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const AccountMapping& account_mapping) const {
3231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int ttl_seconds = account_mapping.status == AccountMapping::REMOVING ?
3241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      kGCMRemoveMappingMessageTTL : kGCMAddMappingMessageTTL;
3251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return account_mapping.status_change_timestamp +
3261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      base::TimeDelta::FromSeconds(ttl_seconds) < clock_->Now();
3271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAccountMapping* GCMAccountMapper::FindMappingByAccountId(
3301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const std::string& account_id) {
3311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for (AccountMappings::iterator iter = accounts_.begin();
3321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       iter != accounts_.end();
3331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       ++iter) {
3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (iter->account_id == account_id)
3351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return &*iter;
3361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
3371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return NULL;
3391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciGCMAccountMapper::AccountMappings::iterator
3421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciGCMAccountMapper::FindMappingByMessageId(const std::string& message_id) {
3431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for (std::vector<AccountMapping>::iterator iter = accounts_.begin();
3441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       iter != accounts_.end();
3451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       ++iter) {
3461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (iter->last_message_id == message_id)
3471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return iter;
3481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
3491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return accounts_.end();
3511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid GCMAccountMapper::SetClockForTesting(scoped_ptr<base::Clock> clock) {
3541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  clock_ = clock.Pass();
3551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}  // namespace gcm
358