diff options
author | fgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-04 19:35:18 +0000 |
---|---|---|
committer | fgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-04 19:35:18 +0000 |
commit | f388235acf9b5d56c4374998240faca888ba98b5 (patch) | |
tree | 62ed43d2c9c4de99e13d2cc0e86f7630a78d7eca /google_apis | |
parent | acad49654e2c232e6efb5ac7392298bbeb234a40 (diff) | |
download | chromium_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
Diffstat (limited to 'google_apis')
-rw-r--r-- | google_apis/gcm/BUILD.gn | 3 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store.cc | 14 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store.h | 15 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store_impl.cc | 116 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store_impl.h | 8 | ||||
-rw-r--r-- | google_apis/gcm/engine/gcm_store_impl_unittest.cc | 81 |
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. |