diff options
author | fgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-03 08:27:23 +0000 |
---|---|---|
committer | fgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-03 08:27:23 +0000 |
commit | 25b5f50e90bbb2b5ca03c1054697e36d093f1ffa (patch) | |
tree | 0a32f2533d8742de8373aa3e50c6d6f030bf064f /google_apis/gcm | |
parent | ddf55bb8b0ced4209979730f71aa9643d5565304 (diff) | |
download | chromium_src-25b5f50e90bbb2b5ca03c1054697e36d093f1ffa.zip chromium_src-25b5f50e90bbb2b5ca03c1054697e36d093f1ffa.tar.gz chromium_src-25b5f50e90bbb2b5ca03c1054697e36d093f1ffa.tar.bz2 |
[GCM] Minimum change for periodic checkin
* enforces checkin every couple of days.
* persists/load the last checkin time from gcm store.
BUG=355814
Review URL: https://codereview.chromium.org/222913004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261324 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'google_apis/gcm')
-rw-r--r-- | google_apis/gcm/engine/gcm_store.h | 6 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store_impl.cc | 58 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store_impl.h | 4 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store_impl_unittest.cc | 23 | ||||
-rw-r--r-- | google_apis/gcm/gcm_client_impl.cc | 60 | ||||
-rw-r--r-- | google_apis/gcm/gcm_client_impl.h | 7 |
6 files changed, 140 insertions, 18 deletions
diff --git a/google_apis/gcm/engine/gcm_store.h b/google_apis/gcm/engine/gcm_store.h index 659fc7f..ccf480b 100644 --- a/google_apis/gcm/engine/gcm_store.h +++ b/google_apis/gcm/engine/gcm_store.h @@ -14,6 +14,7 @@ #include "base/memory/linked_ptr.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/time/time.h" #include "google_apis/gcm/base/gcm_export.h" #include "google_apis/gcm/engine/registration_info.h" #include "google_apis/gcm/protocol/mcs.pb.h" @@ -47,6 +48,7 @@ class GCM_EXPORT GCMStore { RegistrationInfoMap registrations; std::vector<std::string> incoming_messages; OutgoingMessageMap outgoing_messages; + base::Time last_checkin_time; }; typedef std::vector<std::string> PersistentIdList; @@ -101,6 +103,10 @@ class GCM_EXPORT GCMStore { virtual void RemoveOutgoingMessages(const PersistentIdList& persistent_ids, const UpdateCallback& callback) = 0; + // Sets last device's checkin time. + virtual void SetLastCheckinTime(const base::Time& last_checkin_time, + const UpdateCallback& callback) = 0; + private: DISALLOW_COPY_AND_ASSIGN(GCMStore); }; diff --git a/google_apis/gcm/engine/gcm_store_impl.cc b/google_apis/gcm/engine/gcm_store_impl.cc index 7f27287..3a74127 100644 --- a/google_apis/gcm/engine/gcm_store_impl.cc +++ b/google_apis/gcm/engine/gcm_store_impl.cc @@ -53,6 +53,8 @@ const char kOutgoingMsgKeyStart[] = "outgoing1-"; // Key guaranteed to be higher than all outgoing message keys. // Used for limiting iteration. const char kOutgoingMsgKeyEnd[] = "outgoing2-"; +// Key used to timestamp last checkin (marked with G services settings update). +const char kLastCheckinTimeKey[] = "last_checkin_time"; std::string MakeRegistrationKey(const std::string& app_id) { return kRegistrationKeyStart + app_id; @@ -117,7 +119,8 @@ class GCMStoreImpl::Backend const UpdateCallback& callback); void RemoveUserSerialNumber(const std::string& username, const UpdateCallback& callback); - void SetNextSerialNumber(int64 serial_number, const UpdateCallback& callback); + void SetLastCheckinTime(const base::Time& last_checkin_time, + const UpdateCallback& callback); private: friend class base::RefCountedThreadSafe<Backend>; @@ -127,6 +130,7 @@ class GCMStoreImpl::Backend bool LoadRegistrations(RegistrationInfoMap* registrations); bool LoadIncomingMessages(std::vector<std::string>* incoming_messages); bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages); + bool LoadLastCheckinTime(base::Time* last_checkin_time); const base::FilePath path_; scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_; @@ -171,12 +175,14 @@ void GCMStoreImpl::Backend::Load(const LoadCallback& callback) { &result->device_security_token) || !LoadRegistrations(&result->registrations) || !LoadIncomingMessages(&result->incoming_messages) || - !LoadOutgoingMessages(&result->outgoing_messages)) { + !LoadOutgoingMessages(&result->outgoing_messages) || + !LoadLastCheckinTime(&result->last_checkin_time)) { result->device_android_id = 0; result->device_security_token = 0; result->registrations.clear(); result->incoming_messages.clear(); result->outgoing_messages.clear(); + result->last_checkin_time = base::Time::FromInternalValue(0LL); foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result))); @@ -445,6 +451,24 @@ void GCMStoreImpl::Backend::RemoveOutgoingMessages( AppIdToMessageCountMap())); } +void GCMStoreImpl::Backend::SetLastCheckinTime( + const base::Time& last_checkin_time, + const UpdateCallback& callback) { + leveldb::WriteOptions write_options; + write_options.sync = true; + + int64 last_checkin_time_internal = last_checkin_time.ToInternalValue(); + const leveldb::Status s = + db_->Put(write_options, + MakeSlice(kLastCheckinTimeKey), + MakeSlice(base::Int64ToString(last_checkin_time_internal))); + + if (!s.ok()) + LOG(ERROR) << "LevelDB set last checkin time failed: " << s.ToString(); + + foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok())); +} + bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id, uint64* security_token) { leveldb::ReadOptions read_options; @@ -560,6 +584,26 @@ bool GCMStoreImpl::Backend::LoadOutgoingMessages( return true; } +bool GCMStoreImpl::Backend::LoadLastCheckinTime( + base::Time* last_checkin_time) { + leveldb::ReadOptions read_options; + read_options.verify_checksums = true; + + std::string result; + leveldb::Status s = db_->Get(read_options, + MakeSlice(kLastCheckinTimeKey), + &result); + int64 time_internal = 0LL; + if (s.ok() && !base::StringToInt64(result, &time_internal)) + LOG(ERROR) << "Failed to restore last checkin time. Using default = 0."; + + // In case we cannot read last checkin time, we default it to 0, as we don't + // want that situation to cause the whole load to fail. + *last_checkin_time = base::Time::FromInternalValue(time_internal); + + return true; +} + GCMStoreImpl::GCMStoreImpl( bool use_mock_keychain, const base::FilePath& path, @@ -734,6 +778,16 @@ void GCMStoreImpl::RemoveOutgoingMessages( callback))); } +void GCMStoreImpl::SetLastCheckinTime(const base::Time& last_checkin_time, + const UpdateCallback& callback) { + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&GCMStoreImpl::Backend::SetLastCheckinTime, + backend_, + last_checkin_time, + callback)); +} + void GCMStoreImpl::LoadContinuation(const LoadCallback& callback, scoped_ptr<LoadResult> result) { if (!result->success) { diff --git a/google_apis/gcm/engine/gcm_store_impl.h b/google_apis/gcm/engine/gcm_store_impl.h index 971367a..a08a72c 100644 --- a/google_apis/gcm/engine/gcm_store_impl.h +++ b/google_apis/gcm/engine/gcm_store_impl.h @@ -74,6 +74,10 @@ class GCM_EXPORT GCMStoreImpl : public GCMStore { virtual void RemoveOutgoingMessages(const PersistentIdList& persistent_ids, const UpdateCallback& callback) OVERRIDE; + // Sets last device's checkin time. + virtual void SetLastCheckinTime(const base::Time& last_checkin_time, + const UpdateCallback& callback) OVERRIDE; + private: typedef std::map<std::string, int> AppIdToMessageCountMap; diff --git a/google_apis/gcm/engine/gcm_store_impl_unittest.cc b/google_apis/gcm/engine/gcm_store_impl_unittest.cc index 0656ad2..af5fc3a 100644 --- a/google_apis/gcm/engine/gcm_store_impl_unittest.cc +++ b/google_apis/gcm/engine/gcm_store_impl_unittest.cc @@ -108,6 +108,7 @@ TEST_F(GCMStoreImplTest, LoadNew) { EXPECT_EQ(0U, load_result->device_security_token); EXPECT_TRUE(load_result->incoming_messages.empty()); EXPECT_TRUE(load_result->outgoing_messages.empty()); + EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result->last_checkin_time); } TEST_F(GCMStoreImplTest, DeviceCredentials) { @@ -132,6 +133,28 @@ TEST_F(GCMStoreImplTest, DeviceCredentials) { ASSERT_EQ(kDeviceToken, load_result->device_security_token); } +TEST_F(GCMStoreImplTest, LastCheckinTime) { + scoped_ptr<GCMStore> gcm_store(BuildGCMStore()); + scoped_ptr<GCMStore::LoadResult> load_result; + gcm_store->Load(base::Bind( + &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result)); + PumpLoop(); + + base::Time last_checkin_time = base::Time::Now(); + + gcm_store->SetLastCheckinTime( + last_checkin_time, + base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); + PumpLoop(); + + gcm_store = BuildGCMStore().Pass(); + gcm_store->Load(base::Bind( + &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result)); + PumpLoop(); + + ASSERT_EQ(last_checkin_time, load_result->last_checkin_time); +} + TEST_F(GCMStoreImplTest, Registrations) { scoped_ptr<GCMStore> gcm_store(BuildGCMStore()); scoped_ptr<GCMStore::LoadResult> load_result; diff --git a/google_apis/gcm/gcm_client_impl.cc b/google_apis/gcm/gcm_client_impl.cc index 4cc0fdd..9aaa043 100644 --- a/google_apis/gcm/gcm_client_impl.cc +++ b/google_apis/gcm/gcm_client_impl.cc @@ -74,6 +74,7 @@ enum MessageType { const char kMCSEndpointMain[] = "https://mtalk.google.com:5228"; const char kMCSEndpointFallback[] = "https://mtalk.google.com:443"; +const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60LL; // seconds = 2 days. const int kMaxRegistrationRetries = 5; const char kMessageTypeDataMessage[] = "gcm"; const char kMessageTypeDeletedMessagesKey[] = "deleted_messages"; @@ -209,18 +210,20 @@ void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) { } registrations_ = result->registrations; - device_checkin_info_.android_id = result->device_android_id; device_checkin_info_.secret = result->device_security_token; + base::Time last_checkin_time = result->last_checkin_time; InitializeMCSClient(result.Pass()); - if (!device_checkin_info_.IsValid()) { - device_checkin_info_.Reset(); - state_ = INITIAL_DEVICE_CHECKIN; - StartCheckin(device_checkin_info_); + + if (device_checkin_info_.IsValid()) { + SchedulePeriodicCheckin(last_checkin_time); + OnReady(); return; } - OnReady(); + state_ = INITIAL_DEVICE_CHECKIN; + device_checkin_info_.Reset(); + StartCheckin(); } void GCMClientImpl::InitializeMCSClient( @@ -281,14 +284,14 @@ void GCMClientImpl::ResetState() { // TODO(fgorski): reset all of the necessart objects and start over. } -void GCMClientImpl::StartCheckin(const CheckinInfo& checkin_info) { +void GCMClientImpl::StartCheckin() { checkin_request_.reset( new CheckinRequest(base::Bind(&GCMClientImpl::OnCheckinCompleted, weak_ptr_factory_.GetWeakPtr()), kDefaultBackoffPolicy, chrome_build_proto_, - checkin_info.android_id, - checkin_info.secret, + device_checkin_info_.android_id, + device_checkin_info_.secret, account_ids_, url_request_context_getter_)); checkin_request_->Start(); @@ -311,16 +314,43 @@ void GCMClientImpl::OnCheckinCompleted(uint64 android_id, if (state_ == INITIAL_DEVICE_CHECKIN) { OnFirstTimeDeviceCheckinCompleted(checkin_info); } else { + // checkin_info is not expected to change after a periodic checkin as it + // would invalidate the registratoin IDs. DCHECK_EQ(READY, state_); - if (device_checkin_info_.android_id != checkin_info.android_id || - device_checkin_info_.secret != checkin_info.secret) { - ResetState(); - } else { - // TODO(fgorski): Reset the checkin timer. - } + DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id); + DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret); + } + + if (device_checkin_info_.IsValid()) { + base::Time last_checkin_time = clock_->Now(); + gcm_store_->SetLastCheckinTime( + last_checkin_time, + base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback, + weak_ptr_factory_.GetWeakPtr())); + SchedulePeriodicCheckin(last_checkin_time); } } +void GCMClientImpl::SchedulePeriodicCheckin( + const base::Time& last_checkin_time) { + base::TimeDelta time_to_next_checkin = last_checkin_time + + base::TimeDelta::FromSeconds(kDefaultCheckinInterval) - clock_->Now(); + if (time_to_next_checkin < base::TimeDelta::FromSeconds(0L)) + time_to_next_checkin = base::TimeDelta::FromSeconds(0L); + // TODO(fgorski): Make sure that once dynamic events (like accounts list + // change) trigger checkin we reset the timer. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&GCMClientImpl::StartCheckin, + weak_ptr_factory_.GetWeakPtr()), + time_to_next_checkin); +} + +void GCMClientImpl::SetLastCheckinTimeCallback(bool success) { + // TODO(fgorski): This is one of the signals that store needs a rebuild. + DCHECK(success); +} + void GCMClientImpl::SetDeviceCredentialsCallback(bool success) { // TODO(fgorski): This is one of the signals that store needs a rebuild. DCHECK(success); diff --git a/google_apis/gcm/gcm_client_impl.h b/google_apis/gcm/gcm_client_impl.h index 4aa38ba3..0349270 100644 --- a/google_apis/gcm/gcm_client_impl.h +++ b/google_apis/gcm/gcm_client_impl.h @@ -162,12 +162,17 @@ class GCM_EXPORT GCMClientImpl : public GCMClient { void OnReady(); // Starts a first time device checkin. - void StartCheckin(const CheckinInfo& checkin_info); + void StartCheckin(); // Completes the device checkin request. // |android_id| and |security_token| are expected to be non-zero or an error // is triggered. Function also cleans up the pending checkin. void OnCheckinCompleted(uint64 android_id, uint64 security_token); + // Schedules next device checkin, based on |last_checkin_time| and default + // checkin interval. + void SchedulePeriodicCheckin(const base::Time& last_checkin_time); + // Callback for setting last checkin time in the |gcm_store_|. + void SetLastCheckinTimeCallback(bool success); // Callback for persisting device credentials in the |gcm_store_|. void SetDeviceCredentialsCallback(bool success); |