| Index: chrome/browser/chromeos/contacts/google_contact_store.cc
 | 
| diff --git a/chrome/browser/chromeos/contacts/google_contact_store.cc b/chrome/browser/chromeos/contacts/google_contact_store.cc
 | 
| deleted file mode 100644
 | 
| index 52b8ba5c8d2d870e07f072548840050bae20ae97..0000000000000000000000000000000000000000
 | 
| --- a/chrome/browser/chromeos/contacts/google_contact_store.cc
 | 
| +++ /dev/null
 | 
| @@ -1,434 +0,0 @@
 | 
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 | 
| -// Use of this source code is governed by a BSD-style license that can be
 | 
| -// found in the LICENSE file.
 | 
| -
 | 
| -#include "chrome/browser/chromeos/contacts/google_contact_store.h"
 | 
| -
 | 
| -#include <algorithm>
 | 
| -
 | 
| -#include "base/bind.h"
 | 
| -#include "base/files/file_path.h"
 | 
| -#include "base/logging.h"
 | 
| -#include "chrome/browser/browser_process.h"
 | 
| -#include "chrome/browser/chromeos/contacts/contact.pb.h"
 | 
| -#include "chrome/browser/chromeos/contacts/contact_database.h"
 | 
| -#include "chrome/browser/chromeos/contacts/contact_store_observer.h"
 | 
| -#include "chrome/browser/chromeos/contacts/gdata_contacts_service.h"
 | 
| -#include "chrome/browser/chromeos/profiles/profile_util.h"
 | 
| -#include "chrome/browser/profiles/profile.h"
 | 
| -#include "chrome/browser/signin/profile_oauth2_token_service.h"
 | 
| -#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 | 
| -#include "chrome/browser/signin/signin_manager.h"
 | 
| -#include "chrome/browser/signin/signin_manager_factory.h"
 | 
| -#include "content/public/browser/browser_thread.h"
 | 
| -#include "google_apis/drive/auth_service.h"
 | 
| -#include "google_apis/drive/time_util.h"
 | 
| -
 | 
| -using content::BrowserThread;
 | 
| -
 | 
| -namespace contacts {
 | 
| -
 | 
| -namespace {
 | 
| -
 | 
| -// Name of the directory within the profile directory where the contact database
 | 
| -// is stored.
 | 
| -const base::FilePath::CharType kDatabaseDirectoryName[] =
 | 
| -    FILE_PATH_LITERAL("Google Contacts");
 | 
| -
 | 
| -// We wait this long after the last update has completed successfully before
 | 
| -// updating again.
 | 
| -// TODO(derat): Decide what this should be.
 | 
| -const int kUpdateIntervalSec = 600;
 | 
| -
 | 
| -// https://developers.google.com/google-apps/contacts/v3/index says that deleted
 | 
| -// contact (groups?) will only be returned for 30 days after deletion when the
 | 
| -// "showdeleted" parameter is set. If it's been longer than that since the last
 | 
| -// successful update, we do a full refresh to make sure that we haven't missed
 | 
| -// any deletions. Use 29 instead to make sure that we don't run afoul of
 | 
| -// daylight saving time shenanigans or minor skew in the system clock.
 | 
| -const int kForceFullUpdateDays = 29;
 | 
| -
 | 
| -// When an update fails, we initially wait this many seconds before retrying.
 | 
| -// The delay increases exponentially in response to repeated failures.
 | 
| -const int kUpdateFailureInitialRetrySec = 5;
 | 
| -
 | 
| -// Amount by which |update_delay_on_next_failure_| is multiplied on failure.
 | 
| -const int kUpdateFailureBackoffFactor = 2;
 | 
| -
 | 
| -// OAuth2 scope for the Contacts API.
 | 
| -const char kContactsScope[] = "https://www.google.com/m8/feeds/";
 | 
| -
 | 
| -}  // namespace
 | 
| -
 | 
| -GoogleContactStore::TestAPI::TestAPI(GoogleContactStore* store)
 | 
| -    : store_(store) {
 | 
| -  DCHECK(store);
 | 
| -}
 | 
| -
 | 
| -GoogleContactStore::TestAPI::~TestAPI() {
 | 
| -  store_ = NULL;
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::TestAPI::SetDatabase(ContactDatabaseInterface* db) {
 | 
| -  store_->DestroyDatabase();
 | 
| -  store_->db_ = db;
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::TestAPI::SetGDataService(
 | 
| -    GDataContactsServiceInterface* service) {
 | 
| -  store_->gdata_service_.reset(service);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::TestAPI::DoUpdate() {
 | 
| -  store_->UpdateContacts();
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::TestAPI::NotifyAboutNetworkStateChange(bool online) {
 | 
| -  net::NetworkChangeNotifier::ConnectionType type =
 | 
| -      online ?
 | 
| -      net::NetworkChangeNotifier::CONNECTION_UNKNOWN :
 | 
| -      net::NetworkChangeNotifier::CONNECTION_NONE;
 | 
| -  store_->OnConnectionTypeChanged(type);
 | 
| -}
 | 
| -
 | 
| -scoped_ptr<ContactPointers> GoogleContactStore::TestAPI::GetLoadedContacts() {
 | 
| -  scoped_ptr<ContactPointers> contacts(new ContactPointers);
 | 
| -  for (ContactMap::const_iterator it = store_->contacts_.begin();
 | 
| -       it != store_->contacts_.end(); ++it) {
 | 
| -    contacts->push_back(it->second);
 | 
| -  }
 | 
| -  return contacts.Pass();
 | 
| -}
 | 
| -
 | 
| -GoogleContactStore::GoogleContactStore(
 | 
| -    net::URLRequestContextGetter* url_request_context_getter,
 | 
| -    Profile* profile)
 | 
| -    : url_request_context_getter_(url_request_context_getter),
 | 
| -      profile_(profile),
 | 
| -      db_(new ContactDatabase),
 | 
| -      update_delay_on_next_failure_(
 | 
| -          base::TimeDelta::FromSeconds(kUpdateFailureInitialRetrySec)),
 | 
| -      is_online_(true),
 | 
| -      should_update_when_online_(false),
 | 
| -      weak_ptr_factory_(this) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
 | 
| -  is_online_ = !net::NetworkChangeNotifier::IsOffline();
 | 
| -}
 | 
| -
 | 
| -GoogleContactStore::~GoogleContactStore() {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  weak_ptr_factory_.InvalidateWeakPtrs();
 | 
| -  net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
 | 
| -  DestroyDatabase();
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::Init() {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -
 | 
| -  // Create a GData service if one hasn't already been assigned for testing.
 | 
| -  if (!gdata_service_.get()) {
 | 
| -    std::vector<std::string> scopes;
 | 
| -    scopes.push_back(kContactsScope);
 | 
| -
 | 
| -    ProfileOAuth2TokenService* oauth2_service =
 | 
| -        ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
 | 
| -    SigninManagerBase* signin_manager =
 | 
| -        SigninManagerFactory::GetForProfile(profile_);
 | 
| -    gdata_service_.reset(new GDataContactsService(
 | 
| -        url_request_context_getter_,
 | 
| -        new google_apis::AuthService(
 | 
| -            oauth2_service,
 | 
| -            signin_manager->GetAuthenticatedAccountId(),
 | 
| -            url_request_context_getter_, scopes)));
 | 
| -  }
 | 
| -
 | 
| -  base::FilePath db_path = profile_->GetPath().Append(kDatabaseDirectoryName);
 | 
| -  VLOG(1) << "Initializing contact database \"" << db_path.value() << "\" for "
 | 
| -          << profile_->GetProfileName();
 | 
| -  db_->Init(db_path,
 | 
| -            base::Bind(&GoogleContactStore::OnDatabaseInitialized,
 | 
| -                       weak_ptr_factory_.GetWeakPtr()));
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::AppendContacts(ContactPointers* contacts_out) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  DCHECK(contacts_out);
 | 
| -  for (ContactMap::const_iterator it = contacts_.begin();
 | 
| -       it != contacts_.end(); ++it) {
 | 
| -    if (!it->second->deleted())
 | 
| -      contacts_out->push_back(it->second);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -const Contact* GoogleContactStore::GetContactById(
 | 
| -    const std::string& contact_id) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  return contacts_.Find(contact_id);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::AddObserver(ContactStoreObserver* observer) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  DCHECK(observer);
 | 
| -  observers_.AddObserver(observer);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::RemoveObserver(ContactStoreObserver* observer) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  DCHECK(observer);
 | 
| -  observers_.RemoveObserver(observer);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::OnConnectionTypeChanged(
 | 
| -    net::NetworkChangeNotifier::ConnectionType type) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  bool was_online = is_online_;
 | 
| -  is_online_ = (type != net::NetworkChangeNotifier::CONNECTION_NONE);
 | 
| -  if (!was_online && is_online_ && should_update_when_online_) {
 | 
| -    should_update_when_online_ = false;
 | 
| -    UpdateContacts();
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -base::Time GoogleContactStore::GetCurrentTime() const {
 | 
| -  return !current_time_for_testing_.is_null() ?
 | 
| -         current_time_for_testing_ :
 | 
| -         base::Time::Now();
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::DestroyDatabase() {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  if (db_) {
 | 
| -    db_->DestroyOnUIThread();
 | 
| -    db_ = NULL;
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::UpdateContacts() {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -
 | 
| -  // If we're offline, defer the update.
 | 
| -  if (!is_online_) {
 | 
| -    VLOG(1) << "Deferring contact update due to offline state";
 | 
| -    should_update_when_online_ = true;
 | 
| -    return;
 | 
| -  }
 | 
| -
 | 
| -  base::Time min_update_time;
 | 
| -  base::TimeDelta time_since_last_update =
 | 
| -      last_successful_update_start_time_.is_null() ?
 | 
| -      base::TimeDelta() :
 | 
| -      GetCurrentTime() - last_successful_update_start_time_;
 | 
| -
 | 
| -  if (!last_contact_update_time_.is_null() &&
 | 
| -      time_since_last_update <
 | 
| -      base::TimeDelta::FromDays(kForceFullUpdateDays)) {
 | 
| -    // TODO(derat): I'm adding one millisecond to the last update time here as I
 | 
| -    // don't want to re-download the same most-recently-updated contact each
 | 
| -    // time, but what happens if within the same millisecond, contact A is
 | 
| -    // updated, we do a sync, and then contact B is updated? I'm probably being
 | 
| -    // overly paranoid about this.
 | 
| -    min_update_time =
 | 
| -        last_contact_update_time_ + base::TimeDelta::FromMilliseconds(1);
 | 
| -  }
 | 
| -  if (min_update_time.is_null()) {
 | 
| -    VLOG(1) << "Downloading all contacts for " << profile_->GetProfileName();
 | 
| -  } else {
 | 
| -    VLOG(1) << "Downloading contacts updated since "
 | 
| -            << google_apis::util::FormatTimeAsString(min_update_time) << " for "
 | 
| -            << profile_->GetProfileName();
 | 
| -  }
 | 
| -
 | 
| -  gdata_service_->DownloadContacts(
 | 
| -      base::Bind(&GoogleContactStore::OnDownloadSuccess,
 | 
| -                 weak_ptr_factory_.GetWeakPtr(),
 | 
| -                 min_update_time.is_null(),
 | 
| -                 GetCurrentTime()),
 | 
| -      base::Bind(&GoogleContactStore::OnDownloadFailure,
 | 
| -                 weak_ptr_factory_.GetWeakPtr()),
 | 
| -      min_update_time);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::ScheduleUpdate(bool last_update_was_successful) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  base::TimeDelta delay;
 | 
| -  if (last_update_was_successful) {
 | 
| -    delay = base::TimeDelta::FromSeconds(kUpdateIntervalSec);
 | 
| -    update_delay_on_next_failure_ =
 | 
| -        base::TimeDelta::FromSeconds(kUpdateFailureInitialRetrySec);
 | 
| -  } else {
 | 
| -    delay = update_delay_on_next_failure_;
 | 
| -    update_delay_on_next_failure_ = std::min(
 | 
| -        update_delay_on_next_failure_ * kUpdateFailureBackoffFactor,
 | 
| -        base::TimeDelta::FromSeconds(kUpdateIntervalSec));
 | 
| -  }
 | 
| -  VLOG(1) << "Scheduling update of " << profile_->GetProfileName()
 | 
| -          << " in " << delay.InSeconds() << " second(s)";
 | 
| -  update_timer_.Start(
 | 
| -      FROM_HERE, delay, this, &GoogleContactStore::UpdateContacts);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::MergeContacts(
 | 
| -    bool is_full_update,
 | 
| -    scoped_ptr<ScopedVector<Contact> > updated_contacts) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -
 | 
| -  if (is_full_update) {
 | 
| -    contacts_.Clear();
 | 
| -    last_contact_update_time_ = base::Time();
 | 
| -  }
 | 
| -
 | 
| -  // Find the maximum update time from |updated_contacts| since contacts whose
 | 
| -  // |deleted| flags are set won't be saved to |contacts_|.
 | 
| -  for (ScopedVector<Contact>::iterator it = updated_contacts->begin();
 | 
| -       it != updated_contacts->end(); ++it) {
 | 
| -    last_contact_update_time_ =
 | 
| -        std::max(last_contact_update_time_,
 | 
| -                 base::Time::FromInternalValue((*it)->update_time()));
 | 
| -  }
 | 
| -  VLOG(1) << "Last contact update time is "
 | 
| -          << google_apis::util::FormatTimeAsString(last_contact_update_time_);
 | 
| -
 | 
| -  contacts_.Merge(updated_contacts.Pass(), ContactMap::DROP_DELETED_CONTACTS);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::OnDownloadSuccess(
 | 
| -    bool is_full_update,
 | 
| -    const base::Time& update_start_time,
 | 
| -    scoped_ptr<ScopedVector<Contact> > updated_contacts) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  VLOG(1) << "Got " << updated_contacts->size() << " contact(s) for "
 | 
| -          << profile_->GetProfileName();
 | 
| -
 | 
| -  // Copy the pointers so we can update just these contacts in the database.
 | 
| -  scoped_ptr<ContactPointers> contacts_to_save_to_db(new ContactPointers);
 | 
| -  scoped_ptr<ContactDatabaseInterface::ContactIds>
 | 
| -      contact_ids_to_delete_from_db(new ContactDatabaseInterface::ContactIds);
 | 
| -  if (db_) {
 | 
| -    for (size_t i = 0; i < updated_contacts->size(); ++i) {
 | 
| -      Contact* contact = (*updated_contacts)[i];
 | 
| -      if (contact->deleted())
 | 
| -        contact_ids_to_delete_from_db->push_back(contact->contact_id());
 | 
| -      else
 | 
| -        contacts_to_save_to_db->push_back(contact);
 | 
| -    }
 | 
| -  }
 | 
| -  bool got_updates = !updated_contacts->empty();
 | 
| -
 | 
| -  MergeContacts(is_full_update, updated_contacts.Pass());
 | 
| -  last_successful_update_start_time_ = update_start_time;
 | 
| -
 | 
| -  if (is_full_update || got_updates) {
 | 
| -    FOR_EACH_OBSERVER(ContactStoreObserver,
 | 
| -                      observers_,
 | 
| -                      OnContactsUpdated(this));
 | 
| -  }
 | 
| -
 | 
| -  if (db_) {
 | 
| -    // Even if this was an incremental update and we didn't get any updated
 | 
| -    // contacts, we still want to write updated metadata containing
 | 
| -    // |update_start_time|.
 | 
| -    VLOG(1) << "Saving " << contacts_to_save_to_db->size() << " contact(s) to "
 | 
| -            << "database and deleting " << contact_ids_to_delete_from_db->size()
 | 
| -            << " as " << (is_full_update ? "full" : "incremental") << " update";
 | 
| -
 | 
| -    scoped_ptr<UpdateMetadata> metadata(new UpdateMetadata);
 | 
| -    metadata->set_last_update_start_time(update_start_time.ToInternalValue());
 | 
| -    metadata->set_last_contact_update_time(
 | 
| -        last_contact_update_time_.ToInternalValue());
 | 
| -
 | 
| -    db_->SaveContacts(
 | 
| -        contacts_to_save_to_db.Pass(),
 | 
| -        contact_ids_to_delete_from_db.Pass(),
 | 
| -        metadata.Pass(),
 | 
| -        is_full_update,
 | 
| -        base::Bind(&GoogleContactStore::OnDatabaseContactsSaved,
 | 
| -                   weak_ptr_factory_.GetWeakPtr()));
 | 
| -
 | 
| -    // We'll schedule an update from OnDatabaseContactsSaved() after we're done
 | 
| -    // writing to the database -- we don't want to modify the contacts while
 | 
| -    // they're being used by the database.
 | 
| -  } else {
 | 
| -    ScheduleUpdate(true);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::OnDownloadFailure() {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  LOG(WARNING) << "Contacts download failed for " << profile_->GetProfileName();
 | 
| -  ScheduleUpdate(false);
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::OnDatabaseInitialized(bool success) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  if (success) {
 | 
| -    VLOG(1) << "Contact database initialized for "
 | 
| -            << profile_->GetProfileName();
 | 
| -    db_->LoadContacts(base::Bind(&GoogleContactStore::OnDatabaseContactsLoaded,
 | 
| -                                 weak_ptr_factory_.GetWeakPtr()));
 | 
| -  } else {
 | 
| -    LOG(WARNING) << "Failed to initialize contact database for "
 | 
| -                 << profile_->GetProfileName();
 | 
| -    // Limp along as best as we can: throw away the database and do an update,
 | 
| -    // which will schedule further updates.
 | 
| -    DestroyDatabase();
 | 
| -    UpdateContacts();
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::OnDatabaseContactsLoaded(
 | 
| -    bool success,
 | 
| -    scoped_ptr<ScopedVector<Contact> > contacts,
 | 
| -    scoped_ptr<UpdateMetadata> metadata) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  if (success) {
 | 
| -    VLOG(1) << "Loaded " << contacts->size() << " contact(s) from database";
 | 
| -    MergeContacts(true, contacts.Pass());
 | 
| -    last_successful_update_start_time_ =
 | 
| -        base::Time::FromInternalValue(metadata->last_update_start_time());
 | 
| -    last_contact_update_time_ = std::max(
 | 
| -        last_contact_update_time_,
 | 
| -        base::Time::FromInternalValue(metadata->last_contact_update_time()));
 | 
| -
 | 
| -    if (!contacts_.empty()) {
 | 
| -      FOR_EACH_OBSERVER(ContactStoreObserver,
 | 
| -                        observers_,
 | 
| -                        OnContactsUpdated(this));
 | 
| -    }
 | 
| -  } else {
 | 
| -    LOG(WARNING) << "Failed to load contacts from database";
 | 
| -  }
 | 
| -  UpdateContacts();
 | 
| -}
 | 
| -
 | 
| -void GoogleContactStore::OnDatabaseContactsSaved(bool success) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  if (!success)
 | 
| -    LOG(WARNING) << "Failed to save contacts to database";
 | 
| -
 | 
| -  // We only update the database when we've successfully downloaded contacts, so
 | 
| -  // report success to ScheduleUpdate() even if the database update failed.
 | 
| -  ScheduleUpdate(true);
 | 
| -}
 | 
| -
 | 
| -GoogleContactStoreFactory::GoogleContactStoreFactory() {
 | 
| -}
 | 
| -
 | 
| -GoogleContactStoreFactory::~GoogleContactStoreFactory() {
 | 
| -}
 | 
| -
 | 
| -bool GoogleContactStoreFactory::CanCreateContactStoreForProfile(
 | 
| -    Profile* profile) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  DCHECK(profile);
 | 
| -  return chromeos::IsProfileAssociatedWithGaiaAccount(profile);
 | 
| -}
 | 
| -
 | 
| -ContactStore* GoogleContactStoreFactory::CreateContactStore(Profile* profile) {
 | 
| -  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| -  DCHECK(CanCreateContactStoreForProfile(profile));
 | 
| -  return new GoogleContactStore(
 | 
| -      g_browser_process->system_request_context(), profile);
 | 
| -}
 | 
| -
 | 
| -}  // namespace contacts
 | 
| 
 |