summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-04 19:35:18 +0000
committerfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-04 19:35:18 +0000
commitf388235acf9b5d56c4374998240faca888ba98b5 (patch)
tree62ed43d2c9c4de99e13d2cc0e86f7630a78d7eca
parentacad49654e2c232e6efb5ac7392298bbeb234a40 (diff)
downloadchromium_src-f388235acf9b5d56c4374998240faca888ba98b5.zip
chromium_src-f388235acf9b5d56c4374998240faca888ba98b5.tar.gz
chromium_src-f388235acf9b5d56c4374998240faca888ba98b5.tar.bz2
[GCM] Persistence of account mappings.
* Adding ability to add, remove and load account info to device mappings in GCMStore with Tests. * Moving the code resetting load results to LoadResult structure (fixed a bug where last_checkin_accounts where not cleared upon failure to load) R=zea@chromium.org BUG=374969 Review URL: https://codereview.chromium.org/429073002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287391 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--google_apis/gcm/BUILD.gn3
-rw-r--r--google_apis/gcm/engine/gcm_store.cc14
-rw-r--r--google_apis/gcm/engine/gcm_store.h15
-rw-r--r--google_apis/gcm/engine/gcm_store_impl.cc116
-rw-r--r--google_apis/gcm/engine/gcm_store_impl.h8
-rw-r--r--google_apis/gcm/engine/gcm_store_impl_unittest.cc81
6 files changed, 220 insertions, 17 deletions
diff --git a/google_apis/gcm/BUILD.gn b/google_apis/gcm/BUILD.gn
index f69fdfc..47482fd 100644
--- a/google_apis/gcm/BUILD.gn
+++ b/google_apis/gcm/BUILD.gn
@@ -12,6 +12,8 @@ component("gcm") {
"base/mcs_util.h",
"base/socket_stream.cc",
"base/socket_stream.h",
+ "engine/account_info.cc",
+ "engine/account_info.h",
"engine/checkin_request.cc",
"engine/checkin_request.h",
"engine/connection_factory.cc",
@@ -105,6 +107,7 @@ test("gcm_unit_tests") {
"base/mcs_message_unittest.cc",
"base/mcs_util_unittest.cc",
"base/socket_stream_unittest.cc",
+ "engine/account_info_unittest.cc",
"engine/checkin_request_unittest.cc",
"engine/connection_factory_impl_unittest.cc",
"engine/connection_handler_impl_unittest.cc",
diff --git a/google_apis/gcm/engine/gcm_store.cc b/google_apis/gcm/engine/gcm_store.cc
index 91ad60f..8a6bf31 100644
--- a/google_apis/gcm/engine/gcm_store.cc
+++ b/google_apis/gcm/engine/gcm_store.cc
@@ -14,6 +14,20 @@ GCMStore::LoadResult::LoadResult()
GCMStore::LoadResult::~LoadResult() {}
+void GCMStore::LoadResult::Reset() {
+ device_android_id = 0;
+ device_security_token = 0;
+ registrations.clear();
+ incoming_messages.clear();
+ outgoing_messages.clear();
+ gservices_settings.clear();
+ gservices_digest.clear();
+ last_checkin_time = base::Time::FromInternalValue(0LL);
+ last_checkin_accounts.clear();
+ account_infos.clear();
+ success = false;
+}
+
GCMStore::GCMStore() {}
GCMStore::~GCMStore() {}
diff --git a/google_apis/gcm/engine/gcm_store.h b/google_apis/gcm/engine/gcm_store.h
index ca02850..33d6916c 100644
--- a/google_apis/gcm/engine/gcm_store.h
+++ b/google_apis/gcm/engine/gcm_store.h
@@ -19,6 +19,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "google_apis/gcm/base/gcm_export.h"
+#include "google_apis/gcm/engine/account_info.h"
#include "google_apis/gcm/engine/registration_info.h"
namespace gcm {
@@ -33,11 +34,16 @@ class GCM_EXPORT GCMStore {
typedef std::map<std::string, linked_ptr<google::protobuf::MessageLite> >
OutgoingMessageMap;
+ // Map of account id to account info for account mappings.
+ typedef std::map<std::string, AccountInfo> AccountInfoMap;
+
// Container for Load(..) results.
struct GCM_EXPORT LoadResult {
LoadResult();
~LoadResult();
+ void Reset();
+
bool success;
uint64 device_android_id;
uint64 device_security_token;
@@ -48,6 +54,7 @@ class GCM_EXPORT GCMStore {
std::string gservices_digest;
base::Time last_checkin_time;
std::set<std::string> last_checkin_accounts;
+ AccountInfoMap account_infos;
};
typedef std::vector<std::string> PersistentIdList;
@@ -102,7 +109,7 @@ class GCM_EXPORT GCMStore {
virtual void RemoveOutgoingMessages(const PersistentIdList& persistent_ids,
const UpdateCallback& callback) = 0;
- // Sets last device's checkin time.
+ // Sets last device's checkin information.
virtual void SetLastCheckinInfo(const base::Time& time,
const std::set<std::string>& accounts,
const UpdateCallback& callback) = 0;
@@ -115,6 +122,12 @@ class GCM_EXPORT GCMStore {
const std::string& settings_digest,
const UpdateCallback& callback) = 0;
+ // Sets the account information related to device to account mapping.
+ virtual void AddAccountMapping(const AccountInfo& account_info,
+ const UpdateCallback& callback) = 0;
+ virtual void RemoveAccountMapping(const std::string& account_id,
+ 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 df35925..3764d0f 100644
--- a/google_apis/gcm/engine/gcm_store_impl.cc
+++ b/google_apis/gcm/engine/gcm_store_impl.cc
@@ -68,6 +68,12 @@ const char kGServiceSettingsDigestKey[] = "gservices_digest";
const char kLastCheckinAccountsKey[] = "last_checkin_accounts_count";
// Key used to timestamp last checkin (marked with G services settings update).
const char kLastCheckinTimeKey[] = "last_checkin_time";
+// Lowest lexicographically ordered account key.
+// Used for prefixing account information.
+const char kAccountKeyStart[] = "account1-";
+// Key guaranteed to be higher than all account keys.
+// Used for limiting iteration.
+const char kAccountKeyEnd[] = "account2-";
std::string MakeRegistrationKey(const std::string& app_id) {
return kRegistrationKeyStart + app_id;
@@ -97,6 +103,14 @@ std::string ParseGServiceSettingKey(const std::string& key) {
return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
}
+std::string MakeAccountKey(const std::string& account_id) {
+ return kAccountKeyStart + account_id;
+}
+
+std::string ParseAccountKey(const std::string& key) {
+ return key.substr(arraysize(kAccountKeyStart) - 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.
@@ -148,6 +162,10 @@ class GCMStoreImpl::Backend
const std::map<std::string, std::string>& settings,
const std::string& digest,
const UpdateCallback& callback);
+ void AddAccountMapping(const AccountInfo& account_info,
+ const UpdateCallback& callback);
+ void RemoveAccountMapping(const std::string& account_id,
+ const UpdateCallback& callback);
private:
friend class base::RefCountedThreadSafe<Backend>;
@@ -161,6 +179,7 @@ class GCMStoreImpl::Backend
std::set<std::string>* accounts);
bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
std::string* digest);
+ bool LoadAccountMappingInfo(AccountInfoMap* account_infos);
const base::FilePath path_;
scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
@@ -214,15 +233,9 @@ void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
!LoadLastCheckinInfo(&result->last_checkin_time,
&result->last_checkin_accounts) ||
!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);
+ &result->gservices_digest) ||
+ !LoadAccountMappingInfo(&result->account_infos)) {
+ result->Reset();
foreground_task_runner_->PostTask(FROM_HERE,
base::Bind(callback,
base::Passed(&result)));
@@ -561,6 +574,48 @@ void GCMStoreImpl::Backend::SetGServicesSettings(
foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
}
+void GCMStoreImpl::Backend::AddAccountMapping(const AccountInfo& account_info,
+ const UpdateCallback& callback) {
+ DVLOG(1) << "Saving account info for account with email: "
+ << account_info.email;
+ if (!db_.get()) {
+ LOG(ERROR) << "GCMStore db doesn't exist.";
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+
+ std::string data = account_info.SerializeAsString();
+ std::string key = MakeAccountKey(account_info.account_id);
+ const leveldb::Status s =
+ db_->Put(write_options, MakeSlice(key), MakeSlice(data));
+ if (!s.ok())
+ LOG(ERROR) << "LevelDB adding account mapping failed: " << s.ToString();
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
+}
+
+void GCMStoreImpl::Backend::RemoveAccountMapping(
+ const std::string& account_id,
+ const UpdateCallback& callback) {
+ if (!db_.get()) {
+ LOG(ERROR) << "GCMStore db doesn't exist.";
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+
+ leveldb::Status s =
+ db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
+
+ if (!s.ok())
+ LOG(ERROR) << "LevelDB removal of account mapping 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;
@@ -734,6 +789,29 @@ bool GCMStoreImpl::Backend::LoadGServicesSettings(
return true;
}
+bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
+ AccountInfoMap* account_infos) {
+ leveldb::ReadOptions read_options;
+ read_options.verify_checksums = true;
+
+ scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
+ for (iter->Seek(MakeSlice(kAccountKeyStart));
+ iter->Valid() && iter->key().ToString() < kAccountKeyEnd;
+ iter->Next()) {
+ AccountInfo account_info;
+ account_info.account_id = ParseAccountKey(iter->key().ToString());
+ if (!account_info.ParseFromString(iter->value().ToString())) {
+ DVLOG(1) << "Failed to parse account info with ID: "
+ << account_info.account_id;
+ return false;
+ }
+ DVLOG(1) << "Found account mapping with ID: " << account_info.account_id;
+ (*account_infos)[account_info.account_id] = account_info;
+ }
+
+ return true;
+}
+
GCMStoreImpl::GCMStoreImpl(
const base::FilePath& path,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
@@ -933,6 +1011,26 @@ void GCMStoreImpl::SetGServicesSettings(
callback));
}
+void GCMStoreImpl::AddAccountMapping(const AccountInfo& account_info,
+ const UpdateCallback& callback) {
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&GCMStoreImpl::Backend::AddAccountMapping,
+ backend_,
+ account_info,
+ callback));
+}
+
+void GCMStoreImpl::RemoveAccountMapping(const std::string& account_id,
+ const UpdateCallback& callback) {
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping,
+ backend_,
+ account_id,
+ 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 30ce411..c0a8a95 100644
--- a/google_apis/gcm/engine/gcm_store_impl.h
+++ b/google_apis/gcm/engine/gcm_store_impl.h
@@ -76,7 +76,7 @@ class GCM_EXPORT GCMStoreImpl : public GCMStore {
virtual void RemoveOutgoingMessages(const PersistentIdList& persistent_ids,
const UpdateCallback& callback) OVERRIDE;
- // Sets last device's checkin time.
+ // Sets last device's checkin information.
virtual void SetLastCheckinInfo(const base::Time& time,
const std::set<std::string>& accounts,
const UpdateCallback& callback) OVERRIDE;
@@ -87,6 +87,12 @@ class GCM_EXPORT GCMStoreImpl : public GCMStore {
const std::string& settings_digest,
const UpdateCallback& callback) OVERRIDE;
+ // Sets the account information related to device to account mapping.
+ virtual void AddAccountMapping(const AccountInfo& account_info,
+ const UpdateCallback& callback) OVERRIDE;
+ virtual void RemoveAccountMapping(const std::string& account_id,
+ 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 ad4b024..cb47749 100644
--- a/google_apis/gcm/engine/gcm_store_impl_unittest.cc
+++ b/google_apis/gcm/engine/gcm_store_impl_unittest.cc
@@ -45,8 +45,6 @@ class GCMStoreImplTest : public testing::Test {
GCMStoreImplTest();
virtual ~GCMStoreImplTest();
- virtual void SetUp() OVERRIDE;
-
scoped_ptr<GCMStore> BuildGCMStore();
std::string GetNextPersistentId();
@@ -74,10 +72,6 @@ GCMStoreImplTest::GCMStoreImplTest()
GCMStoreImplTest::~GCMStoreImplTest() {}
-void GCMStoreImplTest::SetUp() {
- testing::Test::SetUp();
-}
-
scoped_ptr<GCMStore> GCMStoreImplTest::BuildGCMStore() {
return scoped_ptr<GCMStore>(new GCMStoreImpl(
temp_directory_.path(),
@@ -506,6 +500,81 @@ TEST_F(GCMStoreImplTest, PerAppMessageLimits) {
}
}
+TEST_F(GCMStoreImplTest, AccountMapping) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+
+ // Add account mappings.
+ AccountInfo account_info1;
+ account_info1.account_id = "account_id_1";
+ account_info1.email = "account_id_1@gmail.com";
+ account_info1.last_message_type = AccountInfo::MSG_ADD;
+ account_info1.last_message_id = "message_1";
+ account_info1.last_message_timestamp =
+ base::Time::FromInternalValue(1305797421259935LL);
+ AccountInfo account_info2;
+ account_info2.account_id = "account_id_2";
+ account_info2.email = "account_id_2@gmail.com";
+ account_info2.last_message_type = AccountInfo::MSG_REMOVE;
+ account_info2.last_message_id = "message_2";
+ account_info2.last_message_timestamp =
+ base::Time::FromInternalValue(1305734521259935LL);
+
+ gcm_store->AddAccountMapping(
+ account_info1,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+ gcm_store->AddAccountMapping(
+ account_info2,
+ 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();
+
+ EXPECT_EQ(2UL, load_result->account_infos.size());
+ GCMStore::AccountInfoMap::iterator iter = load_result->account_infos.begin();
+ EXPECT_EQ("account_id_1", iter->first);
+ EXPECT_EQ(account_info1.account_id, iter->second.account_id);
+ EXPECT_EQ(account_info1.email, iter->second.email);
+ EXPECT_EQ(account_info1.last_message_type, iter->second.last_message_type);
+ EXPECT_EQ(account_info1.last_message_id, iter->second.last_message_id);
+ EXPECT_EQ(account_info1.last_message_timestamp,
+ iter->second.last_message_timestamp);
+ ++iter;
+ EXPECT_EQ("account_id_2", iter->first);
+ EXPECT_EQ(account_info2.account_id, iter->second.account_id);
+ EXPECT_EQ(account_info2.email, iter->second.email);
+ EXPECT_EQ(account_info2.last_message_type, iter->second.last_message_type);
+ EXPECT_EQ(account_info2.last_message_id, iter->second.last_message_id);
+ EXPECT_EQ(account_info2.last_message_timestamp,
+ iter->second.last_message_timestamp);
+
+ gcm_store->RemoveAccountMapping(
+ account_info1.account_id,
+ 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();
+
+ EXPECT_EQ(1UL, load_result->account_infos.size());
+ iter = load_result->account_infos.begin();
+ EXPECT_EQ("account_id_2", iter->first);
+ EXPECT_EQ(account_info2.account_id, iter->second.account_id);
+ EXPECT_EQ(account_info2.email, iter->second.email);
+ EXPECT_EQ(account_info2.last_message_type, iter->second.last_message_type);
+ EXPECT_EQ(account_info2.last_message_id, iter->second.last_message_id);
+ EXPECT_EQ(account_info2.last_message_timestamp,
+ iter->second.last_message_timestamp);
+}
+
// When the database is destroyed, all database updates should fail. At the
// same time, they per-app message counts should not go up, as failures should
// result in decrementing the counts.