summaryrefslogtreecommitdiffstats
path: root/google_apis/gcm
diff options
context:
space:
mode:
authorfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-03 08:27:23 +0000
committerfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-03 08:27:23 +0000
commit25b5f50e90bbb2b5ca03c1054697e36d093f1ffa (patch)
tree0a32f2533d8742de8373aa3e50c6d6f030bf064f /google_apis/gcm
parentddf55bb8b0ced4209979730f71aa9643d5565304 (diff)
downloadchromium_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.h6
-rw-r--r--google_apis/gcm/engine/gcm_store_impl.cc58
-rw-r--r--google_apis/gcm/engine/gcm_store_impl.h4
-rw-r--r--google_apis/gcm/engine/gcm_store_impl_unittest.cc23
-rw-r--r--google_apis/gcm/gcm_client_impl.cc60
-rw-r--r--google_apis/gcm/gcm_client_impl.h7
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);