summaryrefslogtreecommitdiffstats
path: root/google_apis/gcm
diff options
context:
space:
mode:
authorfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-04 23:03:55 +0000
committerfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-04 23:03:55 +0000
commitd3ba08d9ead26a59ae5503ca7f175c5a6046f2cc (patch)
tree36df33b1f4903d8236f7f74d1e822081f02b47b9 /google_apis/gcm
parent1590d95a9dc56331266b5927e5d7f81eaa395e5a (diff)
downloadchromium_src-d3ba08d9ead26a59ae5503ca7f175c5a6046f2cc.zip
chromium_src-d3ba08d9ead26a59ae5503ca7f175c5a6046f2cc.tar.gz
chromium_src-d3ba08d9ead26a59ae5503ca7f175c5a6046f2cc.tar.bz2
Adding periodic checkin controlled by G-services settings
* extracting CheckinRequest::RequestInfo to better manage checkin parameters * adding G-services settings digest to RequestInfo of the checkin (will be sent with checkin request) * extracting G-services settings from checkin response * storing and loading of the G-services settings in GCM Store BUG=359254 Review URL: https://codereview.chromium.org/215363007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261912 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'google_apis/gcm')
-rw-r--r--google_apis/gcm/engine/checkin_request.cc42
-rw-r--r--google_apis/gcm/engine/checkin_request.h45
-rw-r--r--google_apis/gcm/engine/checkin_request_unittest.cc37
-rw-r--r--google_apis/gcm/engine/gcm_store.h10
-rw-r--r--google_apis/gcm/engine/gcm_store_impl.cc108
-rw-r--r--google_apis/gcm/engine/gcm_store_impl.h6
-rw-r--r--google_apis/gcm/engine/gcm_store_impl_unittest.cc50
-rw-r--r--google_apis/gcm/gcm_client_impl.cc63
-rw-r--r--google_apis/gcm/gcm_client_impl.h32
-rw-r--r--google_apis/gcm/gcm_client_impl_unittest.cc146
-rw-r--r--google_apis/gcm/tools/mcs_probe.cc31
11 files changed, 481 insertions, 89 deletions
diff --git a/google_apis/gcm/engine/checkin_request.cc b/google_apis/gcm/engine/checkin_request.cc
index 94a6fc9..7addbeb 100644
--- a/google_apis/gcm/engine/checkin_request.cc
+++ b/google_apis/gcm/engine/checkin_request.cc
@@ -46,21 +46,30 @@ void RecordCheckinStatusToUMA(CheckinRequestStatus status) {
} // namespace
-CheckinRequest::CheckinRequest(
- const CheckinRequestCallback& callback,
- const net::BackoffEntry::Policy& backoff_policy,
- const checkin_proto::ChromeBuildProto& chrome_build_proto,
+CheckinRequest::RequestInfo::RequestInfo(
uint64 android_id,
uint64 security_token,
+ const std::string& settings_digest,
const std::vector<std::string>& account_ids,
+ const checkin_proto::ChromeBuildProto& chrome_build_proto)
+ : android_id(android_id),
+ security_token(security_token),
+ settings_digest(settings_digest),
+ account_ids(account_ids),
+ chrome_build_proto(chrome_build_proto) {
+}
+
+CheckinRequest::RequestInfo::~RequestInfo() {}
+
+CheckinRequest::CheckinRequest(
+ const RequestInfo& request_info,
+ const net::BackoffEntry::Policy& backoff_policy,
+ const CheckinRequestCallback& callback,
net::URLRequestContextGetter* request_context_getter)
: request_context_getter_(request_context_getter),
callback_(callback),
backoff_entry_(&backoff_policy),
- chrome_build_proto_(chrome_build_proto),
- android_id_(android_id),
- security_token_(security_token),
- account_ids_(account_ids),
+ request_info_(request_info),
weak_ptr_factory_(this) {
}
@@ -70,21 +79,24 @@ void CheckinRequest::Start() {
DCHECK(!url_fetcher_.get());
checkin_proto::AndroidCheckinRequest request;
- request.set_id(android_id_);
- request.set_security_token(security_token_);
+ request.set_id(request_info_.android_id);
+ request.set_security_token(request_info_.security_token);
request.set_user_serial_number(kDefaultUserSerialNumber);
request.set_version(kRequestVersionValue);
+ if (!request_info_.settings_digest.empty())
+ request.set_digest(request_info_.settings_digest);
checkin_proto::AndroidCheckinProto* checkin = request.mutable_checkin();
- checkin->mutable_chrome_build()->CopyFrom(chrome_build_proto_);
+ checkin->mutable_chrome_build()->CopyFrom(request_info_.chrome_build_proto);
#if defined(CHROME_OS)
checkin->set_type(checkin_proto::DEVICE_CHROME_OS);
#else
checkin->set_type(checkin_proto::DEVICE_CHROME_BROWSER);
#endif
- for (std::vector<std::string>::const_iterator iter = account_ids_.begin();
- iter != account_ids_.end();
+ for (std::vector<std::string>::const_iterator iter =
+ request_info_.account_ids.begin();
+ iter != request_info_.account_ids.end();
++iter) {
request.add_account_cookie("[" + *iter + "]");
}
@@ -141,7 +153,7 @@ void CheckinRequest::OnURLFetchComplete(const net::URLFetcher* source) {
<< response_status << ". Checkin failed.";
RecordCheckinStatusToUMA(response_status == net::HTTP_BAD_REQUEST ?
HTTP_BAD_REQUEST : HTTP_UNAUTHORIZED);
- callback_.Run(0,0);
+ callback_.Run(response_proto);
return;
}
@@ -167,7 +179,7 @@ void CheckinRequest::OnURLFetchComplete(const net::URLFetcher* source) {
}
RecordCheckinStatusToUMA(SUCCESS);
- callback_.Run(response_proto.android_id(), response_proto.security_token());
+ callback_.Run(response_proto);
}
} // namespace gcm
diff --git a/google_apis/gcm/engine/checkin_request.h b/google_apis/gcm/engine/checkin_request.h
index e11089a..1d706a7 100644
--- a/google_apis/gcm/engine/checkin_request.h
+++ b/google_apis/gcm/engine/checkin_request.h
@@ -12,7 +12,7 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "google_apis/gcm/base/gcm_export.h"
-#include "google_apis/gcm/protocol/android_checkin.pb.h"
+#include "google_apis/gcm/protocol/checkin.pb.h"
#include "net/base/backoff_entry.h"
#include "net/url_request/url_fetcher_delegate.h"
@@ -28,17 +28,35 @@ namespace gcm {
// check-ins.
class GCM_EXPORT CheckinRequest : public net::URLFetcherDelegate {
public:
- // A callback function for the checkin request, accepting |android_id| and
- // |security_token|.
- typedef base::Callback<void(uint64 android_id, uint64 security_token)>
- CheckinRequestCallback;
-
- CheckinRequest(const CheckinRequestCallback& callback,
+ // A callback function for the checkin request, accepting |checkin_response|
+ // protobuf.
+ typedef base::Callback<void(const checkin_proto::AndroidCheckinResponse&
+ checkin_response)> CheckinRequestCallback;
+
+ // Checkin request details.
+ struct GCM_EXPORT RequestInfo {
+ RequestInfo(uint64 android_id,
+ uint64 security_token,
+ const std::string& settings_digest,
+ const std::vector<std::string>& account_ids,
+ const checkin_proto::ChromeBuildProto& chrome_build_proto);
+ ~RequestInfo();
+
+ // Android ID of the device.
+ uint64 android_id;
+ // Security token of the device.
+ uint64 security_token;
+ // Digest of GServices settings on the device.
+ std::string settings_digest;
+ // Account IDs of GAIA accounts related to this device.
+ std::vector<std::string> account_ids;
+ // Information of the Chrome build of this device.
+ checkin_proto::ChromeBuildProto chrome_build_proto;
+ };
+
+ CheckinRequest(const RequestInfo& request_info,
const net::BackoffEntry::Policy& backoff_policy,
- const checkin_proto::ChromeBuildProto& chrome_build_proto,
- uint64 android_id,
- uint64 security_token,
- const std::vector<std::string>& account_ids,
+ const CheckinRequestCallback& callback,
net::URLRequestContextGetter* request_context_getter);
virtual ~CheckinRequest();
@@ -57,10 +75,7 @@ class GCM_EXPORT CheckinRequest : public net::URLFetcherDelegate {
net::BackoffEntry backoff_entry_;
scoped_ptr<net::URLFetcher> url_fetcher_;
- const checkin_proto::ChromeBuildProto chrome_build_proto_;
- const uint64 android_id_;
- const uint64 security_token_;
- const std::vector<std::string> account_ids_;
+ const RequestInfo request_info_;
base::WeakPtrFactory<CheckinRequest> weak_ptr_factory_;
diff --git a/google_apis/gcm/engine/checkin_request_unittest.cc b/google_apis/gcm/engine/checkin_request_unittest.cc
index 0142e0c..e750df0 100644
--- a/google_apis/gcm/engine/checkin_request_unittest.cc
+++ b/google_apis/gcm/engine/checkin_request_unittest.cc
@@ -51,6 +51,7 @@ const uint64 kBlankAndroidId = 999999UL;
const uint64 kBlankSecurityToken = 999999UL;
const char kChromeVersion[] = "Version String";
const uint64 kSecurityToken = 77;
+const char kSettingsDigest[] = "settings_digest";
class CheckinRequestTest : public testing::Test {
public:
@@ -65,7 +66,8 @@ class CheckinRequestTest : public testing::Test {
CheckinRequestTest();
virtual ~CheckinRequestTest();
- void FetcherCallback(uint64 android_id, uint64 security_token);
+ void FetcherCallback(
+ const checkin_proto::AndroidCheckinResponse& response);
void CreateRequest(uint64 android_id, uint64 security_token);
@@ -102,11 +104,13 @@ CheckinRequestTest::CheckinRequestTest()
CheckinRequestTest::~CheckinRequestTest() {}
-void CheckinRequestTest::FetcherCallback(uint64 android_id,
- uint64 security_token) {
+void CheckinRequestTest::FetcherCallback(
+ const checkin_proto::AndroidCheckinResponse& checkin_response) {
callback_called_ = true;
- android_id_ = android_id;
- security_token_ = security_token;
+ if (checkin_response.has_android_id())
+ android_id_ = checkin_response.android_id();
+ if (checkin_response.has_security_token())
+ security_token_ = checkin_response.security_token();
}
void CheckinRequestTest::CreateRequest(uint64 android_id,
@@ -117,15 +121,19 @@ void CheckinRequestTest::CreateRequest(uint64 android_id,
chrome_build_proto_.set_channel(
checkin_proto::ChromeBuildProto::CHANNEL_CANARY);
chrome_build_proto_.set_chrome_version(kChromeVersion);
+
+ CheckinRequest::RequestInfo request_info(
+ android_id,
+ security_token,
+ kSettingsDigest,
+ account_ids_,
+ chrome_build_proto_);
// Then create a request with that protobuf and specified android_id,
// security_token.
request_.reset(new CheckinRequest(
- base::Bind(&CheckinRequestTest::FetcherCallback, base::Unretained(this)),
+ request_info,
kDefaultBackoffPolicy,
- chrome_build_proto_,
- android_id,
- security_token,
- account_ids_,
+ base::Bind(&CheckinRequestTest::FetcherCallback, base::Unretained(this)),
url_request_context_getter_.get()));
// Setting android_id_ and security_token_ to blank value, not used elsewhere
@@ -198,6 +206,7 @@ TEST_F(CheckinRequestTest, FetcherData) {
request_proto.checkin().type());
#endif
+ EXPECT_EQ(kSettingsDigest, request_proto.digest());
EXPECT_EQ(1, request_proto.account_cookie_size());
EXPECT_EQ("[account_id]", request_proto.account_cookie(0));
}
@@ -244,8 +253,8 @@ TEST_F(CheckinRequestTest, ResponseHttpStatusUnauthorized) {
CompleteFetch();
EXPECT_TRUE(callback_called_);
- EXPECT_EQ(0u, android_id_);
- EXPECT_EQ(0u, security_token_);
+ EXPECT_EQ(kBlankAndroidId, android_id_);
+ EXPECT_EQ(kBlankSecurityToken, security_token_);
}
TEST_F(CheckinRequestTest, ResponseHttpStatusBadRequest) {
@@ -256,8 +265,8 @@ TEST_F(CheckinRequestTest, ResponseHttpStatusBadRequest) {
CompleteFetch();
EXPECT_TRUE(callback_called_);
- EXPECT_EQ(0u, android_id_);
- EXPECT_EQ(0u, security_token_);
+ EXPECT_EQ(kBlankAndroidId, android_id_);
+ EXPECT_EQ(kBlankSecurityToken, security_token_);
}
TEST_F(CheckinRequestTest, ResponseHttpStatusNotOK) {
diff --git a/google_apis/gcm/engine/gcm_store.h b/google_apis/gcm/engine/gcm_store.h
index ccf480b..1b15e56b 100644
--- a/google_apis/gcm/engine/gcm_store.h
+++ b/google_apis/gcm/engine/gcm_store.h
@@ -48,6 +48,8 @@ class GCM_EXPORT GCMStore {
RegistrationInfoMap registrations;
std::vector<std::string> incoming_messages;
OutgoingMessageMap outgoing_messages;
+ std::map<std::string, std::string> gservices_settings;
+ std::string gservices_digest;
base::Time last_checkin_time;
};
@@ -107,6 +109,14 @@ class GCM_EXPORT GCMStore {
virtual void SetLastCheckinTime(const base::Time& last_checkin_time,
const UpdateCallback& callback) = 0;
+ // G-service settings handling.
+ // Persists |settings| and |settings_digest|. It completely replaces the
+ // existing data.
+ virtual void SetGServicesSettings(
+ const std::map<std::string, std::string>& settings,
+ const std::string& settings_digest,
+ 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 9bd562d..5393fd1 100644
--- a/google_apis/gcm/engine/gcm_store_impl.cc
+++ b/google_apis/gcm/engine/gcm_store_impl.cc
@@ -16,12 +16,14 @@
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
+#include "base/time/time.h"
#include "base/tracked_objects.h"
#include "components/os_crypt/os_crypt.h"
#include "google_apis/gcm/base/mcs_message.h"
#include "google_apis/gcm/base/mcs_util.h"
#include "google_apis/gcm/protocol/mcs.pb.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace gcm {
@@ -53,6 +55,14 @@ const char kOutgoingMsgKeyStart[] = "outgoing1-";
// Key guaranteed to be higher than all outgoing message keys.
// Used for limiting iteration.
const char kOutgoingMsgKeyEnd[] = "outgoing2-";
+// Lowest lexicographically ordered G-service settings key.
+// Used for prefixing G-services settings.
+const char kGServiceSettingKeyStart[] = "gservice1-";
+// Key guaranteed to be higher than all G-services settings keys.
+// Used for limiting iteration.
+const char kGServiceSettingKeyEnd[] = "gservice2-";
+// Key for digest of the last G-services settings update.
+const char kGServiceSettingsDigestKey[] = "gservices_digest";
// Key used to timestamp last checkin (marked with G services settings update).
const char kLastCheckinTimeKey[] = "last_checkin_time";
@@ -76,6 +86,14 @@ std::string ParseOutgoingKey(const std::string& key) {
return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
}
+std::string MakeGServiceSettingKey(const std::string& setting_name) {
+ return kGServiceSettingKeyStart + setting_name;
+}
+
+std::string ParseGServiceSettingKey(const std::string& key) {
+ return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
+}
+
// Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
// outlive the slice.
// For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
@@ -121,6 +139,10 @@ class GCMStoreImpl::Backend
const UpdateCallback& callback);
void SetLastCheckinTime(const base::Time& last_checkin_time,
const UpdateCallback& callback);
+ void SetGServicesSettings(
+ const std::map<std::string, std::string>& settings,
+ const std::string& digest,
+ const UpdateCallback& callback);
private:
friend class base::RefCountedThreadSafe<Backend>;
@@ -131,6 +153,8 @@ class GCMStoreImpl::Backend
bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
bool LoadLastCheckinTime(base::Time* last_checkin_time);
+ bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
+ std::string* digest);
const base::FilePath path_;
scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
@@ -176,12 +200,16 @@ void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
!LoadRegistrations(&result->registrations) ||
!LoadIncomingMessages(&result->incoming_messages) ||
!LoadOutgoingMessages(&result->outgoing_messages) ||
- !LoadLastCheckinTime(&result->last_checkin_time)) {
+ !LoadLastCheckinTime(&result->last_checkin_time) ||
+ !LoadGServicesSettings(&result->gservices_settings,
+ &result->gservices_digest)) {
result->device_android_id = 0;
result->device_security_token = 0;
result->registrations.clear();
result->incoming_messages.clear();
result->outgoing_messages.clear();
+ result->gservices_settings.clear();
+ result->gservices_digest.clear();
result->last_checkin_time = base::Time::FromInternalValue(0LL);
foreground_task_runner_->PostTask(FROM_HERE,
base::Bind(callback,
@@ -465,7 +493,44 @@ void GCMStoreImpl::Backend::SetLastCheckinTime(
if (!s.ok())
LOG(ERROR) << "LevelDB set last checkin time failed: " << s.ToString();
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
+}
+
+void GCMStoreImpl::Backend::SetGServicesSettings(
+ const std::map<std::string, std::string>& settings,
+ const std::string& settings_digest,
+ const UpdateCallback& callback) {
+ leveldb::WriteBatch write_batch;
+
+ // Remove all existing settings.
+ leveldb::ReadOptions read_options;
+ read_options.verify_checksums = true;
+ scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
+ for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
+ iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
+ iter->Next()) {
+ write_batch.Delete(iter->key());
+ }
+
+ // Add the new settings.
+ for (std::map<std::string, std::string>::const_iterator iter =
+ settings.begin();
+ iter != settings.end(); ++iter) {
+ write_batch.Put(MakeSlice(MakeGServiceSettingKey(iter->first)),
+ MakeSlice(iter->second));
+ }
+
+ // Update the settings digest.
+ write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
+ MakeSlice(settings_digest));
+
+ // Write it all in a batch.
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+ leveldb::Status s = db_->Write(write_options, &write_batch);
+ if (!s.ok())
+ LOG(ERROR) << "LevelDB GService Settings update failed: " << s.ToString();
foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
}
@@ -604,6 +669,34 @@ bool GCMStoreImpl::Backend::LoadLastCheckinTime(
return true;
}
+bool GCMStoreImpl::Backend::LoadGServicesSettings(
+ std::map<std::string, std::string>* settings,
+ std::string* digest) {
+ leveldb::ReadOptions read_options;
+ read_options.verify_checksums = true;
+
+ // Load all of the GServices settings.
+ scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
+ for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
+ iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
+ iter->Next()) {
+ std::string value = iter->value().ToString();
+ if (value.empty()) {
+ LOG(ERROR) << "Error reading GService Settings " << value;
+ return false;
+ }
+ std::string id = ParseGServiceSettingKey(iter->key().ToString());
+ (*settings)[id] = value;
+ DVLOG(1) << "Found G Service setting with key: " << id
+ << ", and value: " << value;
+ }
+
+ // Load the settings digest. It's ok if it is empty.
+ db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
+
+ return true;
+}
+
GCMStoreImpl::GCMStoreImpl(
const base::FilePath& path,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
@@ -783,6 +876,19 @@ void GCMStoreImpl::SetLastCheckinTime(const base::Time& last_checkin_time,
callback));
}
+void GCMStoreImpl::SetGServicesSettings(
+ const std::map<std::string, std::string>& settings,
+ const std::string& digest,
+ const UpdateCallback& callback) {
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
+ backend_,
+ settings,
+ digest,
+ 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 a1eeff6..e46c503 100644
--- a/google_apis/gcm/engine/gcm_store_impl.h
+++ b/google_apis/gcm/engine/gcm_store_impl.h
@@ -77,6 +77,12 @@ class GCM_EXPORT GCMStoreImpl : public GCMStore {
virtual void SetLastCheckinTime(const base::Time& last_checkin_time,
const UpdateCallback& callback) OVERRIDE;
+ // G-service settings handling.
+ virtual void SetGServicesSettings(
+ const std::map<std::string, std::string>& settings,
+ const std::string& settings_digest,
+ 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 41bb427..f0f9019 100644
--- a/google_apis/gcm/engine/gcm_store_impl_unittest.cc
+++ b/google_apis/gcm/engine/gcm_store_impl_unittest.cc
@@ -111,6 +111,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_TRUE(load_result->gservices_settings.empty());
EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result->last_checkin_time);
}
@@ -154,10 +155,57 @@ TEST_F(GCMStoreImplTest, LastCheckinTime) {
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, GServicesSettings_ProtocolV2) {
+ 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();
+
+ std::map<std::string, std::string> settings;
+ settings["checkin_interval"] = "12345";
+ settings["mcs_port"] = "438";
+ settings["checkin_url"] = "http://checkin.google.com";
+ std::string digest = "digest1";
+
+ gcm_store->SetGServicesSettings(
+ settings,
+ digest,
+ 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(settings, load_result->gservices_settings);
+ ASSERT_EQ(digest, load_result->gservices_digest);
+
+ // Remove some, and add some.
+ settings.clear();
+ settings["checkin_interval"] = "54321";
+ settings["registration_url"] = "http://registration.google.com";
+ digest = "digest2";
+
+ gcm_store->SetGServicesSettings(
+ settings,
+ digest,
+ 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(settings, load_result->gservices_settings);
+ ASSERT_EQ(digest, load_result->gservices_digest);
+}
+
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 568e740..78b7da1 100644
--- a/google_apis/gcm/gcm_client_impl.cc
+++ b/google_apis/gcm/gcm_client_impl.cc
@@ -11,6 +11,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
#include "base/time/default_clock.h"
#include "google_apis/gcm/base/mcs_message.h"
#include "google_apis/gcm/base/mcs_util.h"
@@ -221,6 +222,8 @@ void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
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;
+ gservices_settings_ = result->gservices_settings;
+ gservices_digest_ = result->gservices_digest;
InitializeMCSClient(result.Pass());
if (device_checkin_info_.IsValid()) {
@@ -293,32 +296,36 @@ void GCMClientImpl::ResetState() {
}
void GCMClientImpl::StartCheckin() {
+ CheckinRequest::RequestInfo request_info(
+ device_checkin_info_.android_id,
+ device_checkin_info_.secret,
+ gservices_digest_,
+ account_ids_,
+ chrome_build_proto_);
checkin_request_.reset(
- new CheckinRequest(base::Bind(&GCMClientImpl::OnCheckinCompleted,
- weak_ptr_factory_.GetWeakPtr()),
+ new CheckinRequest(request_info,
kDefaultBackoffPolicy,
- chrome_build_proto_,
- device_checkin_info_.android_id,
- device_checkin_info_.secret,
- account_ids_,
+ base::Bind(&GCMClientImpl::OnCheckinCompleted,
+ weak_ptr_factory_.GetWeakPtr()),
url_request_context_getter_));
checkin_request_->Start();
}
-void GCMClientImpl::OnCheckinCompleted(uint64 android_id,
- uint64 security_token) {
+void GCMClientImpl::OnCheckinCompleted(
+ const checkin_proto::AndroidCheckinResponse& checkin_response) {
checkin_request_.reset();
- CheckinInfo checkin_info;
- checkin_info.android_id = android_id;
- checkin_info.secret = security_token;
-
- if (!checkin_info.IsValid()) {
- // TODO(fgorski): I don't think a retry here will help, we should probalby
+ if (!checkin_response.has_android_id() ||
+ !checkin_response.has_security_token()) {
+ // TODO(fgorski): I don't think a retry here will help, we should probably
// start over. By checking in with (0, 0).
return;
}
+ CheckinInfo checkin_info;
+ checkin_info.android_id = checkin_response.android_id();
+ checkin_info.secret = checkin_response.security_token();
+
if (state_ == INITIAL_DEVICE_CHECKIN) {
OnFirstTimeDeviceCheckinCompleted(checkin_info);
} else {
@@ -335,6 +342,7 @@ void GCMClientImpl::OnCheckinCompleted(uint64 android_id,
last_checkin_time,
base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
weak_ptr_factory_.GetWeakPtr()));
+ UpdateGServicesSettings(checkin_response);
SchedulePeriodicCheckin(last_checkin_time);
}
}
@@ -702,4 +710,31 @@ void GCMClientImpl::HandleIncomingSendError(
send_error_details);
}
+void GCMClientImpl::UpdateGServicesSettings(
+ const checkin_proto::AndroidCheckinResponse& checkin_response) {
+ if (!checkin_response.has_digest() ||
+ checkin_response.digest() == gservices_digest_) {
+ return;
+ }
+
+ gservices_digest_ = checkin_response.digest();
+ gservices_settings_.clear();
+
+ for (int i = 0; i < checkin_response.setting_size(); ++i) {
+ std::string name = checkin_response.setting(i).name();
+ std::string value = checkin_response.setting(i).value();
+ gservices_settings_[name] = value;
+ }
+
+ gcm_store_->SetGServicesSettings(
+ gservices_settings_,
+ gservices_digest_,
+ base::Bind(&GCMClientImpl::UpdateGServicesSettingsCallback,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void GCMClientImpl::UpdateGServicesSettingsCallback(bool success) {
+ DCHECK(success);
+}
+
} // namespace gcm
diff --git a/google_apis/gcm/gcm_client_impl.h b/google_apis/gcm/gcm_client_impl.h
index 51ca7be..7b28a34 100644
--- a/google_apis/gcm/gcm_client_impl.h
+++ b/google_apis/gcm/gcm_client_impl.h
@@ -19,7 +19,7 @@
#include "google_apis/gcm/engine/registration_request.h"
#include "google_apis/gcm/engine/unregistration_request.h"
#include "google_apis/gcm/gcm_client.h"
-#include "google_apis/gcm/protocol/android_checkin.pb.h"
+#include "google_apis/gcm/protocol/checkin.pb.h"
#include "net/base/net_log.h"
#include "net/url_request/url_request_context_getter.h"
@@ -40,6 +40,10 @@ class CheckinRequest;
class ConnectionFactory;
class GCMClientImplTest;
+// Map with GServices settings.
+// TODO(fgorski): Extract services handling to a separate struct/class.
+typedef std::map<std::string, std::string> GServicesSettingsMap;
+
// Helper class for building GCM internals. Allows tests to inject fake versions
// as necessary.
class GCM_EXPORT GCMInternalsBuilder {
@@ -167,13 +171,12 @@ class GCM_EXPORT GCMClientImpl : public GCMClient {
// Starts a first time device checkin.
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.
+ // Completes the device checkin request by parsing the |checkin_response|.
+ // Function also cleans up the pending checkin.
+ void OnCheckinCompleted(
+ const checkin_proto::AndroidCheckinResponse& checkin_response);
+ // Schedules next device checkin, based on |last_checkin_time| and
+ // checkin_interval specified in GServices settings.
void SchedulePeriodicCheckin(const base::Time& last_checkin_time);
// Callback for setting last checkin time in the |gcm_store_|.
void SetLastCheckinTimeCallback(bool success);
@@ -212,6 +215,13 @@ class GCM_EXPORT GCMClientImpl : public GCMClient {
const mcs_proto::DataMessageStanza& data_message_stanza,
MessageData& message_data);
+ // Updates the G-services settings based on the |checkin_response|. It assumes
+ // base::Time::Now() is the checkin time.
+ void UpdateGServicesSettings(
+ const checkin_proto::AndroidCheckinResponse& checkin_response);
+ // Completes the G-services settings update request.
+ void UpdateGServicesSettingsCallback(bool success);
+
// Builder for the GCM internals (mcs client, etc.).
scoped_ptr<GCMInternalsBuilder> internals_builder_;
@@ -240,6 +250,12 @@ class GCM_EXPORT GCMClientImpl : public GCMClient {
scoped_ptr<ConnectionFactory> connection_factory_;
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ // Map with GServices settings, such us checkin_interval, checkin_url, etc.
+ GServicesSettingsMap gservices_settings_;
+
+ // Digest of the |gservices_settings|.
+ std::string gservices_digest_;
+
// Controls receiving and sending of packets and reliable message queueing.
scoped_ptr<MCSClient> mcs_client_;
diff --git a/google_apis/gcm/gcm_client_impl_unittest.cc b/google_apis/gcm/gcm_client_impl_unittest.cc
index 39fb3f1..2520382 100644
--- a/google_apis/gcm/gcm_client_impl_unittest.cc
+++ b/google_apis/gcm/gcm_client_impl_unittest.cc
@@ -7,6 +7,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
#include "base/test/simple_test_clock.h"
#include "components/os_crypt/os_crypt.h"
#include "google_apis/gcm/base/mcs_message.h"
@@ -38,6 +39,9 @@ enum LastEvent {
const uint64 kDeviceAndroidId = 54321;
const uint64 kDeviceSecurityToken = 12345;
+const int64 kSettingsCheckinInterval = 0;
+const char kSettingsCheckinIntervalKey[] = "checkin_interval";
+const char kSettingsDefaultDigest[] = "default_digest";
const char kAppId[] = "app_id";
const char kSender[] = "project_id";
const char kSender2[] = "project_id2";
@@ -183,7 +187,11 @@ class GCMClientImplTest : public testing::Test,
void BuildGCMClient();
void InitializeGCMClient();
void ReceiveMessageFromMCS(const MCSMessage& message);
- void CompleteCheckin(uint64 android_id, uint64 security_token);
+ void CompleteCheckin(
+ uint64 android_id,
+ uint64 security_token,
+ const std::string& digest,
+ const std::map<std::string, std::string>& settings);
void CompleteRegistration(const std::string& registration_id);
void CompleteUnregistration(const std::string& app_id);
@@ -217,6 +225,12 @@ class GCMClientImplTest : public testing::Test,
ConnectionFactory* connection_factory() const {
return gcm_client_->connection_factory_.get();
}
+ GServicesSettingsMap& services_settings() {
+ return gcm_client_->gservices_settings_;
+ }
+ const std::string& services_digest() {
+ return gcm_client_->gservices_digest_;
+ }
void reset_last_event() {
last_event_ = NONE;
@@ -239,19 +253,21 @@ class GCMClientImplTest : public testing::Test,
const GCMClient::SendErrorDetails& last_error_details() const {
return last_error_details_;
}
+ base::SimpleTestClock* clock() const {
+ return reinterpret_cast<base::SimpleTestClock*>(gcm_client_->clock_.get());
+ }
int64 CurrentTime();
- private:
// Tooling.
void PumpLoop();
void PumpLoopUntilIdle();
void QuitLoop();
+ void ResetLoop();
- base::SimpleTestClock* clock() const {
- return reinterpret_cast<base::SimpleTestClock*>(gcm_client_->clock_.get());
- }
+ bool CreateUniqueTempDir();
+ private:
// Variables used for verification.
LastEvent last_event_;
std::string last_app_id_;
@@ -282,11 +298,14 @@ GCMClientImplTest::GCMClientImplTest()
GCMClientImplTest::~GCMClientImplTest() {}
void GCMClientImplTest::SetUp() {
- ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
- run_loop_.reset(new base::RunLoop);
+ ASSERT_TRUE(CreateUniqueTempDir());
+ ResetLoop();
BuildGCMClient();
InitializeGCMClient();
- CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken);
+ CompleteCheckin(kDeviceAndroidId,
+ kDeviceSecurityToken,
+ kSettingsDefaultDigest,
+ std::map<std::string, std::string>());
}
void GCMClientImplTest::PumpLoop() {
@@ -304,18 +323,38 @@ void GCMClientImplTest::QuitLoop() {
run_loop_->Quit();
}
+void GCMClientImplTest::ResetLoop() {
+ run_loop_.reset(new base::RunLoop);
+}
+
+bool GCMClientImplTest::CreateUniqueTempDir() {
+ return temp_directory_.CreateUniqueTempDir();
+}
+
void GCMClientImplTest::BuildGCMClient() {
gcm_client_.reset(new GCMClientImpl(
make_scoped_ptr<GCMInternalsBuilder>(new FakeGCMInternalsBuilder())));
}
-void GCMClientImplTest::CompleteCheckin(uint64 android_id,
- uint64 security_token) {
+void GCMClientImplTest::CompleteCheckin(
+ uint64 android_id,
+ uint64 security_token,
+ const std::string& digest,
+ const std::map<std::string, std::string>& settings) {
checkin_proto::AndroidCheckinResponse response;
response.set_stats_ok(true);
response.set_android_id(android_id);
response.set_security_token(security_token);
+ // For testing GServices settings.
+ response.set_digest(digest);
+ for (std::map<std::string, std::string>::const_iterator it = settings.begin();
+ it != settings.end(); ++it) {
+ checkin_proto::GservicesSetting* setting = response.add_setting();
+ setting->set_name(it->first);
+ setting->set_value(it->second);
+ }
+
std::string response_string;
response.SerializeToString(&response_string);
@@ -610,4 +649,91 @@ TEST_F(GCMClientImplTest, SendMessage) {
mcs_client()->last_data_message_stanza().app_data(0).value());
}
+class GCMClientImplCheckinTest : public GCMClientImplTest {
+ public:
+ GCMClientImplCheckinTest();
+ virtual ~GCMClientImplCheckinTest();
+
+ virtual void SetUp() OVERRIDE;
+
+ std::map<std::string, std::string> GenerateSettings();
+};
+
+GCMClientImplCheckinTest::GCMClientImplCheckinTest() {}
+
+GCMClientImplCheckinTest::~GCMClientImplCheckinTest() {}
+
+void GCMClientImplCheckinTest::SetUp() {
+ ASSERT_TRUE(CreateUniqueTempDir());
+ ResetLoop();
+ BuildGCMClient();
+ InitializeGCMClient();
+}
+
+std::map<std::string, std::string>
+GCMClientImplCheckinTest::GenerateSettings() {
+ std::map<std::string, std::string> settings;
+ settings[kSettingsCheckinIntervalKey] =
+ base::Int64ToString(kSettingsCheckinInterval);
+ return settings;
+}
+
+TEST_F(GCMClientImplCheckinTest, GServicesSettingsAfterInitialCheckin) {
+ CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
+ kSettingsDefaultDigest, GenerateSettings());
+ EXPECT_EQ(base::Int64ToString(kSettingsCheckinInterval),
+ services_settings()[kSettingsCheckinIntervalKey]);
+}
+
+// This test only checks that periodic checkin happens.
+TEST_F(GCMClientImplCheckinTest, PeriodicCheckin) {
+ std::map<std::string, std::string> settings = GenerateSettings();
+ CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
+ kSettingsDefaultDigest, settings);
+ PumpLoopUntilIdle();
+
+ CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
+ kSettingsDefaultDigest, settings);
+ }
+
+// This test checks that checkin reponse with the same digest will not update
+// G-services settings.
+TEST_F(GCMClientImplCheckinTest, GServicesSettingsSameDigest) {
+ std::map<std::string, std::string> settings = GenerateSettings();
+ settings["checkin_url"] = "http://checkin.google.com";
+ CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
+ kSettingsDefaultDigest, settings);
+ EXPECT_EQ(settings, services_settings());
+ EXPECT_EQ(kSettingsDefaultDigest, services_digest());
+ PumpLoopUntilIdle();
+
+ // Response will carry same digest and no settings.
+ CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
+ kSettingsDefaultDigest, std::map<std::string, std::string>());
+ EXPECT_EQ(settings, services_settings());
+ EXPECT_EQ(kSettingsDefaultDigest, services_digest());
+}
+
+// Test that checkin response with a different digest will also update the
+// G-services settings.
+TEST_F(GCMClientImplCheckinTest, GServicesSettingsDifferentDigest) {
+ std::map<std::string, std::string> settings = GenerateSettings();
+ settings["checkin_url"] = "http://checkin.google.com";
+ CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
+ kSettingsDefaultDigest, settings);
+ EXPECT_EQ(settings, services_settings());
+ EXPECT_EQ(kSettingsDefaultDigest, services_digest());
+ PumpLoopUntilIdle();
+
+ settings.clear();
+ settings["some_settings"] = "on second checkin";
+ settings[kSettingsCheckinIntervalKey] = "2100";
+ settings["checkin_url"] = "http://checkin.google.com";
+ std::string new_digest = "some_other_digest";
+
+ CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken, new_digest, settings);
+ EXPECT_EQ(settings, services_settings());
+ EXPECT_EQ(new_digest, services_digest());
+}
+
} // namespace gcm
diff --git a/google_apis/gcm/tools/mcs_probe.cc b/google_apis/gcm/tools/mcs_probe.cc
index 6d9a7d5..038f3af 100644
--- a/google_apis/gcm/tools/mcs_probe.cc
+++ b/google_apis/gcm/tools/mcs_probe.cc
@@ -210,7 +210,8 @@ class MCSProbe {
void LoadCallback(scoped_ptr<GCMStore::LoadResult> load_result);
void UpdateCallback(bool success);
void ErrorCallback();
- void OnCheckInCompleted(uint64 android_id, uint64 secret);
+ void OnCheckInCompleted(
+ const checkin_proto::AndroidCheckinResponse& checkin_response);
void StartMCSLogin();
base::DefaultClock clock_;
@@ -423,24 +424,32 @@ void MCSProbe::CheckIn() {
chrome_build_proto.set_channel(
checkin_proto::ChromeBuildProto::CHANNEL_CANARY);
chrome_build_proto.set_chrome_version(kChromeVersion);
+
+ CheckinRequest::RequestInfo request_info(
+ 0, 0, std::string(), std::vector<std::string>(), chrome_build_proto);
+
checkin_request_.reset(new CheckinRequest(
- base::Bind(&MCSProbe::OnCheckInCompleted, base::Unretained(this)),
+ request_info,
kDefaultBackoffPolicy,
- chrome_build_proto,
- 0,
- 0,
- std::vector<std::string>(),
+ base::Bind(&MCSProbe::OnCheckInCompleted, base::Unretained(this)),
url_request_context_getter_.get()));
checkin_request_->Start();
}
-void MCSProbe::OnCheckInCompleted(uint64 android_id, uint64 secret) {
+void MCSProbe::OnCheckInCompleted(
+ const checkin_proto::AndroidCheckinResponse& checkin_response) {
+ bool success = checkin_response.has_android_id() &&
+ checkin_response.android_id() != 0UL &&
+ checkin_response.has_security_token() &&
+ checkin_response.security_token() != 0UL;
LOG(INFO) << "Check-in request completion "
- << (android_id ? "success!" : "failure!");
- if (!android_id || !secret)
+ << (success ? "success!" : "failure!");
+
+ if (!success)
return;
- android_id_ = android_id;
- secret_ = secret;
+
+ android_id_ = checkin_response.android_id();
+ secret_ = checkin_response.security_token();
gcm_store_->SetDeviceCredentials(android_id_,
secret_,