Commit f212ac25 authored by derat@chromium.org's avatar derat@chromium.org

contacts: Defer updates when offline.

This make GoogleContactStore postpone trying to update
contacts via GData when the system doesn't have a network
connection.

BUG=128805
TEST=none


Review URL: https://chromiumcodereview.appspot.com/10829247

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150643 0039d316-1c4b-4281-b951-d872f2087c98
parent 0ca83807
...@@ -74,19 +74,32 @@ void GoogleContactStore::TestAPI::DoUpdate() { ...@@ -74,19 +74,32 @@ void GoogleContactStore::TestAPI::DoUpdate() {
store_->UpdateContacts(); store_->UpdateContacts();
} }
void GoogleContactStore::TestAPI::NotifyAboutNetworkStateChange(bool online) {
net::NetworkChangeNotifier::ConnectionType type =
online ?
net::NetworkChangeNotifier::CONNECTION_UNKNOWN :
net::NetworkChangeNotifier::CONNECTION_NONE;
store_->OnConnectionTypeChanged(type);
}
GoogleContactStore::GoogleContactStore(Profile* profile) GoogleContactStore::GoogleContactStore(Profile* profile)
: profile_(profile), : profile_(profile),
contacts_deleter_(&contacts_), contacts_deleter_(&contacts_),
db_(new ContactDatabase), db_(new ContactDatabase),
update_delay_on_next_failure_( update_delay_on_next_failure_(
base::TimeDelta::FromSeconds(kUpdateFailureInitialRetrySec)), base::TimeDelta::FromSeconds(kUpdateFailureInitialRetrySec)),
is_online_(true),
should_update_when_online_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
is_online_ = !net::NetworkChangeNotifier::IsOffline();
} }
GoogleContactStore::~GoogleContactStore() { GoogleContactStore::~GoogleContactStore() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
weak_ptr_factory_.InvalidateWeakPtrs(); weak_ptr_factory_.InvalidateWeakPtrs();
net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
DestroyDatabase(); DestroyDatabase();
} }
...@@ -129,6 +142,17 @@ void GoogleContactStore::RemoveObserver(ContactStoreObserver* observer) { ...@@ -129,6 +142,17 @@ void GoogleContactStore::RemoveObserver(ContactStoreObserver* observer) {
observers_.RemoveObserver(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 { base::Time GoogleContactStore::GetCurrentTime() const {
return !current_time_for_testing_.is_null() ? return !current_time_for_testing_.is_null() ?
current_time_for_testing_ : current_time_for_testing_ :
...@@ -145,6 +169,14 @@ void GoogleContactStore::DestroyDatabase() { ...@@ -145,6 +169,14 @@ void GoogleContactStore::DestroyDatabase() {
void GoogleContactStore::UpdateContacts() { void GoogleContactStore::UpdateContacts() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 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::Time min_update_time;
base::TimeDelta time_since_last_update = base::TimeDelta time_since_last_update =
last_successful_update_start_time_.is_null() ? last_successful_update_start_time_.is_null() ?
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/time.h" #include "base/time.h"
#include "base/timer.h" #include "base/timer.h"
#include "net/base/network_change_notifier.h"
class Profile; class Profile;
...@@ -34,7 +35,9 @@ class ContactDatabaseInterface; ...@@ -34,7 +35,9 @@ class ContactDatabaseInterface;
class UpdateMetadata; class UpdateMetadata;
// A collection of contacts from a Google account. // A collection of contacts from a Google account.
class GoogleContactStore : public ContactStore { class GoogleContactStore
: public ContactStore,
public net::NetworkChangeNotifier::ConnectionTypeObserver {
public: public:
class TestAPI { class TestAPI {
public: public:
...@@ -56,6 +59,9 @@ class GoogleContactStore : public ContactStore { ...@@ -56,6 +59,9 @@ class GoogleContactStore : public ContactStore {
// Triggers an update, similar to what happens when the update timer fires. // Triggers an update, similar to what happens when the update timer fires.
void DoUpdate(); void DoUpdate();
// Notifies the store that the system has gone online or offline.
void NotifyAboutNetworkStateChange(bool online);
private: private:
GoogleContactStore* store_; // not owned GoogleContactStore* store_; // not owned
...@@ -73,6 +79,10 @@ class GoogleContactStore : public ContactStore { ...@@ -73,6 +79,10 @@ class GoogleContactStore : public ContactStore {
virtual void AddObserver(ContactStoreObserver* observer) OVERRIDE; virtual void AddObserver(ContactStoreObserver* observer) OVERRIDE;
virtual void RemoveObserver(ContactStoreObserver* observer) OVERRIDE; virtual void RemoveObserver(ContactStoreObserver* observer) OVERRIDE;
// net::NetworkChangeNotifier::ConnectionTypeObserver implementation:
virtual void OnConnectionTypeChanged(
net::NetworkChangeNotifier::ConnectionType type) OVERRIDE;
private: private:
// Map from a contact's Google-assigned ID to the contact itself. // Map from a contact's Google-assigned ID to the contact itself.
typedef std::map<std::string, Contact*> ContactMap; typedef std::map<std::string, Contact*> ContactMap;
...@@ -150,6 +160,14 @@ class GoogleContactStore : public ContactStore { ...@@ -150,6 +160,14 @@ class GoogleContactStore : public ContactStore {
// fails. // fails.
base::TimeDelta update_delay_on_next_failure_; base::TimeDelta update_delay_on_next_failure_;
// Do we believe that it's likely that we'll be able to make network
// connections?
bool is_online_;
// Should we call UpdateContacts() when |is_online_| becomes true? Set when
// UpdateContacts() is called while we're offline.
bool should_update_when_online_;
// If non-null, used in place of base::Time::Now() when the current time is // If non-null, used in place of base::Time::Now() when the current time is
// needed. // needed.
base::Time current_time_for_testing_; base::Time current_time_for_testing_;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "chrome/browser/chromeos/gdata/gdata_util.h" #include "chrome/browser/chromeos/gdata/gdata_util.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h" #include "content/public/test/test_browser_thread.h"
#include "net/base/network_change_notifier.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
using content::BrowserThread; using content::BrowserThread;
...@@ -55,7 +56,9 @@ class GoogleContactStoreTest : public testing::Test { ...@@ -55,7 +56,9 @@ class GoogleContactStoreTest : public testing::Test {
protected: protected:
// testing::Test implementation. // testing::Test implementation.
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
testing::Test::SetUp(); // Create a mock NetworkChangeNotifier so the store won't be notified about
// changes to the system's actual network state.
network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
profile_.reset(new TestingProfile); profile_.reset(new TestingProfile);
store_.reset(new GoogleContactStore(profile_.get())); store_.reset(new GoogleContactStore(profile_.get()));
...@@ -74,6 +77,7 @@ class GoogleContactStoreTest : public testing::Test { ...@@ -74,6 +77,7 @@ class GoogleContactStoreTest : public testing::Test {
content::TestBrowserThread ui_thread_; content::TestBrowserThread ui_thread_;
TestContactStoreObserver observer_; TestContactStoreObserver observer_;
scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
scoped_ptr<TestingProfile> profile_; scoped_ptr<TestingProfile> profile_;
scoped_ptr<GoogleContactStore> store_; scoped_ptr<GoogleContactStore> store_;
scoped_ptr<GoogleContactStore::TestAPI> test_api_; scoped_ptr<GoogleContactStore::TestAPI> test_api_;
...@@ -377,5 +381,35 @@ TEST_F(GoogleContactStoreTest, HandleDatabaseInitFailure) { ...@@ -377,5 +381,35 @@ TEST_F(GoogleContactStoreTest, HandleDatabaseInitFailure) {
EXPECT_TRUE(test_api_->update_scheduled()); EXPECT_TRUE(test_api_->update_scheduled());
} }
TEST_F(GoogleContactStoreTest, AvoidUpdatesWhenOffline) {
EXPECT_EQ(0, gdata_service_->num_download_requests());
// Notify the store that we're offline. Init() shouldn't attempt an update
// and the update timer shouldn't be running.
test_api_->NotifyAboutNetworkStateChange(false);
store_->Init();
EXPECT_EQ(0, gdata_service_->num_download_requests());
EXPECT_FALSE(test_api_->update_scheduled());
// We should do an update and schedule further updates as soon as we go
// online.
gdata_service_->reset_stats();
test_api_->NotifyAboutNetworkStateChange(true);
EXPECT_EQ(1, gdata_service_->num_download_requests());
EXPECT_TRUE(test_api_->update_scheduled());
// If we call DoUpdate() to mimic the code path that's used for a timer-driven
// update while we're offline, we should again defer the update.
gdata_service_->reset_stats();
test_api_->NotifyAboutNetworkStateChange(false);
test_api_->DoUpdate();
EXPECT_EQ(0, gdata_service_->num_download_requests());
// When we're back online, the update should happen.
gdata_service_->reset_stats();
test_api_->NotifyAboutNetworkStateChange(true);
EXPECT_EQ(1, gdata_service_->num_download_requests());
}
} // namespace test } // namespace test
} // namespace contacts } // namespace contacts
...@@ -14,7 +14,8 @@ using content::BrowserThread; ...@@ -14,7 +14,8 @@ using content::BrowserThread;
namespace gdata { namespace gdata {
GDataContactsServiceStub::GDataContactsServiceStub() GDataContactsServiceStub::GDataContactsServiceStub()
: download_should_succeed_(true) { : num_download_requests_(0),
download_should_succeed_(true) {
} }
GDataContactsServiceStub::~GDataContactsServiceStub() { GDataContactsServiceStub::~GDataContactsServiceStub() {
...@@ -35,6 +36,7 @@ void GDataContactsServiceStub::DownloadContacts( ...@@ -35,6 +36,7 @@ void GDataContactsServiceStub::DownloadContacts(
FailureCallback failure_callback, FailureCallback failure_callback,
const base::Time& min_update_time) { const base::Time& min_update_time) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
num_download_requests_++;
if (!download_should_succeed_) { if (!download_should_succeed_) {
failure_callback.Run(); failure_callback.Run();
......
...@@ -24,6 +24,8 @@ class GDataContactsServiceStub : public GDataContactsServiceInterface { ...@@ -24,6 +24,8 @@ class GDataContactsServiceStub : public GDataContactsServiceInterface {
GDataContactsServiceStub(); GDataContactsServiceStub();
virtual ~GDataContactsServiceStub(); virtual ~GDataContactsServiceStub();
int num_download_requests() const { return num_download_requests_; }
void reset_stats() { num_download_requests_ = 0; }
void set_download_should_succeed(bool succeed) { void set_download_should_succeed(bool succeed) {
download_should_succeed_ = succeed; download_should_succeed_ = succeed;
} }
...@@ -40,6 +42,9 @@ class GDataContactsServiceStub : public GDataContactsServiceInterface { ...@@ -40,6 +42,9 @@ class GDataContactsServiceStub : public GDataContactsServiceInterface {
const base::Time& min_update_time) OVERRIDE; const base::Time& min_update_time) OVERRIDE;
private: private:
// How many times has DownloadContacts() been called?
int num_download_requests_;
// Should calls to DownloadContacts() succeed? // Should calls to DownloadContacts() succeed?
bool download_should_succeed_; bool download_should_succeed_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment