diff options
34 files changed, 724 insertions, 99 deletions
diff --git a/chrome/browser/extensions/api/instance_id/instance_id_api.cc b/chrome/browser/extensions/api/instance_id/instance_id_api.cc index 4c15782..c57b4ed 100644 --- a/chrome/browser/extensions/api/instance_id/instance_id_api.cc +++ b/chrome/browser/extensions/api/instance_id/instance_id_api.cc @@ -81,8 +81,13 @@ InstanceIDGetIDFunction::InstanceIDGetIDFunction() {} InstanceIDGetIDFunction::~InstanceIDGetIDFunction() {} ExtensionFunction::ResponseAction InstanceIDGetIDFunction::DoWork() { - return RespondNow( - OneArgument(new base::StringValue(GetInstanceID()->GetID()))); + GetInstanceID()->GetID( + base::Bind(&InstanceIDGetIDFunction::GetIDCompleted, this)); + return RespondLater(); +} + +void InstanceIDGetIDFunction::GetIDCompleted(const std::string& id) { + Respond(OneArgument(new base::StringValue(id))); } InstanceIDGetCreationTimeFunction::InstanceIDGetCreationTimeFunction() {} @@ -90,9 +95,15 @@ InstanceIDGetCreationTimeFunction::InstanceIDGetCreationTimeFunction() {} InstanceIDGetCreationTimeFunction::~InstanceIDGetCreationTimeFunction() {} ExtensionFunction::ResponseAction InstanceIDGetCreationTimeFunction::DoWork() { - return RespondNow(OneArgument( - new base::FundamentalValue( - GetInstanceID()->GetCreationTime().ToDoubleT()))); + GetInstanceID()->GetCreationTime( + base::Bind(&InstanceIDGetCreationTimeFunction::GetCreationTimeCompleted, + this)); + return RespondLater(); +} + +void InstanceIDGetCreationTimeFunction::GetCreationTimeCompleted( + const base::Time& creation_time) { + Respond(OneArgument(new base::FundamentalValue(creation_time.ToDoubleT()))); } InstanceIDGetTokenFunction::InstanceIDGetTokenFunction() {} diff --git a/chrome/browser/extensions/api/instance_id/instance_id_api.h b/chrome/browser/extensions/api/instance_id/instance_id_api.h index 9bb404a..6ae1f9d0 100644 --- a/chrome/browser/extensions/api/instance_id/instance_id_api.h +++ b/chrome/browser/extensions/api/instance_id/instance_id_api.h @@ -47,6 +47,8 @@ class InstanceIDGetIDFunction : public InstanceIDApiFunction { ResponseAction DoWork() override; private: + void GetIDCompleted(const std::string& id); + DISALLOW_COPY_AND_ASSIGN(InstanceIDGetIDFunction); }; @@ -64,6 +66,8 @@ class InstanceIDGetCreationTimeFunction : public InstanceIDApiFunction { ResponseAction DoWork() override; private: + void GetCreationTimeCompleted(const base::Time& creation_time); + DISALLOW_COPY_AND_ASSIGN(InstanceIDGetCreationTimeFunction); }; diff --git a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc index 6a3c4ab..a8c00fb 100644 --- a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc +++ b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc @@ -12,12 +12,24 @@ #include "chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h" #include "extensions/test/result_catcher.h" using extensions::ResultCatcher; namespace extensions { +namespace { + +KeyedService* BuildFakeGCMProfileService(content::BrowserContext* context) { + gcm::FakeGCMProfileService* service = + new gcm::FakeGCMProfileService(Profile::FromBrowserContext(context)); + service->SetDriverForTesting(new instance_id::FakeGCMDriverForInstanceID()); + return service; +} + +} // namespace + class InstanceIDApiTest : public ExtensionApiTest { public: InstanceIDApiTest() {} @@ -32,7 +44,7 @@ class InstanceIDApiTest : public ExtensionApiTest { void InstanceIDApiTest::SetUpOnMainThread() { gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory( - browser()->profile(), &gcm::FakeGCMProfileService::Build); + browser()->profile(), &BuildFakeGCMProfileService); ExtensionApiTest::SetUpOnMainThread(); } diff --git a/components/gcm_driver.gypi b/components/gcm_driver.gypi index 4b5e011..b0c90b7 100644 --- a/components/gcm_driver.gypi +++ b/components/gcm_driver.gypi @@ -167,6 +167,7 @@ 'target_name': 'instance_id_test_support', 'type': 'static_library', 'dependencies': [ + 'gcm_driver_test_support', 'instance_id', '../testing/gtest.gyp:gtest', ], @@ -175,8 +176,8 @@ ], 'sources': [ # Note: file list duplicated in GN build. - 'gcm_driver/instance_id/fake_instance_id_driver.cc', - 'gcm_driver/instance_id/fake_instance_id_driver.h', + 'gcm_driver/instance_id/fake_gcm_driver_for_instance_id.cc', + 'gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h', ], }, ], diff --git a/components/gcm_driver/fake_gcm_client.cc b/components/gcm_driver/fake_gcm_client.cc index 1795288..ffa77d1 100644 --- a/components/gcm_driver/fake_gcm_client.cc +++ b/components/gcm_driver/fake_gcm_client.cc @@ -137,6 +137,17 @@ void FakeGCMClient::SetLastTokenFetchTime(const base::Time& time) { void FakeGCMClient::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) { } +void FakeGCMClient::AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) { +} + +void FakeGCMClient::RemoveInstanceIDData(const std::string& app_id) { +} + +std::string FakeGCMClient::GetInstanceIDData(const std::string& app_id) { + return std::string(); +} + void FakeGCMClient::PerformDelayedStart() { DCHECK(ui_thread_->RunsTasksOnCurrentThread()); diff --git a/components/gcm_driver/fake_gcm_client.h b/components/gcm_driver/fake_gcm_client.h index 21d696d..c0b03da 100644 --- a/components/gcm_driver/fake_gcm_client.h +++ b/components/gcm_driver/fake_gcm_client.h @@ -56,6 +56,10 @@ class FakeGCMClient : public GCMClient { void RemoveAccountMapping(const std::string& account_id) override; void SetLastTokenFetchTime(const base::Time& time) override; void UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) override; + void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) override; + void RemoveInstanceIDData(const std::string& app_id) override; + std::string GetInstanceIDData(const std::string& app_id) override; // Initiate the start that has been delayed. // Called on UI thread. diff --git a/components/gcm_driver/fake_gcm_driver.cc b/components/gcm_driver/fake_gcm_driver.cc index 45791f6..c6f7d9e 100644 --- a/components/gcm_driver/fake_gcm_driver.cc +++ b/components/gcm_driver/fake_gcm_driver.cc @@ -98,4 +98,8 @@ void FakeGCMDriver::SetLastTokenFetchTime(const base::Time& time) { void FakeGCMDriver::WakeFromSuspendForHeartbeat(bool wake) { } +InstanceIDStore* FakeGCMDriver::GetInstanceIDStore() { + return NULL; +} + } // namespace gcm diff --git a/components/gcm_driver/fake_gcm_driver.h b/components/gcm_driver/fake_gcm_driver.h index 9b57af2e..5e334c6 100644 --- a/components/gcm_driver/fake_gcm_driver.h +++ b/components/gcm_driver/fake_gcm_driver.h @@ -41,6 +41,7 @@ class FakeGCMDriver : public GCMDriver { base::Time GetLastTokenFetchTime() override; void SetLastTokenFetchTime(const base::Time& time) override; void WakeFromSuspendForHeartbeat(bool wake) override; + InstanceIDStore* GetInstanceIDStore() override; protected: // GCMDriver implementation: diff --git a/components/gcm_driver/gcm_client.h b/components/gcm_driver/gcm_client.h index 2aa224a..cd804b2 100644 --- a/components/gcm_driver/gcm_client.h +++ b/components/gcm_driver/gcm_client.h @@ -309,6 +309,17 @@ class GCMClient { // Updates the timer used by the HeartbeatManager for sending heartbeats. virtual void UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) = 0; + + // Adds the Instance ID data for a specific app to the persistent store. + virtual void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) = 0; + + // Removes the Instance ID data for a specific app from the persistent store. + virtual void RemoveInstanceIDData(const std::string& app_id) = 0; + + // Retrieves the Instance ID data for a specific app from the persistent + // store. + virtual std::string GetInstanceIDData(const std::string& app_id) = 0; }; } // namespace gcm diff --git a/components/gcm_driver/gcm_client_impl.cc b/components/gcm_driver/gcm_client_impl.cc index 1c9f64f..8fb162d 100644 --- a/components/gcm_driver/gcm_client_impl.cc +++ b/components/gcm_driver/gcm_client_impl.cc @@ -348,6 +348,7 @@ void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) { device_checkin_info_.accounts_set = true; last_checkin_time_ = result->last_checkin_time; gservices_settings_.UpdateFromLoadResult(*result); + instance_id_data_ = result->instance_id_data; load_result_ = result.Pass(); state_ = LOADED; @@ -530,6 +531,31 @@ void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) { mcs_client_->UpdateHeartbeatTimer(timer.Pass()); } +void GCMClientImpl::AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) { + instance_id_data_[app_id] = instance_id_data; + gcm_store_->AddInstanceIDData( + app_id, + instance_id_data, + base::Bind(&GCMClientImpl::IgnoreWriteResultCallback, + weak_ptr_factory_.GetWeakPtr())); +} + +void GCMClientImpl::RemoveInstanceIDData(const std::string& app_id) { + instance_id_data_.erase(app_id); + gcm_store_->RemoveInstanceIDData( + app_id, + base::Bind(&GCMClientImpl::IgnoreWriteResultCallback, + weak_ptr_factory_.GetWeakPtr())); +} + +std::string GCMClientImpl::GetInstanceIDData(const std::string& app_id) { + auto iter = instance_id_data_.find(app_id); + if (iter == instance_id_data_.end()) + return std::string(); + return iter->second; +} + void GCMClientImpl::StartCheckin() { // Make sure no checkin is in progress. if (checkin_request_.get()) diff --git a/components/gcm_driver/gcm_client_impl.h b/components/gcm_driver/gcm_client_impl.h index 65b04a6..2db1805 100644 --- a/components/gcm_driver/gcm_client_impl.h +++ b/components/gcm_driver/gcm_client_impl.h @@ -126,6 +126,10 @@ class GCMClientImpl void RemoveAccountMapping(const std::string& account_id) override; void SetLastTokenFetchTime(const base::Time& time) override; void UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) override; + void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) override; + void RemoveInstanceIDData(const std::string& app_id) override; + std::string GetInstanceIDData(const std::string& app_id) override; // GCMStatsRecorder::Delegate implemenation. void OnActivityRecorded() override; @@ -341,6 +345,9 @@ class GCMClientImpl // Time of the last successful checkin. base::Time last_checkin_time_; + // Cached instance ID data, key is app id. + std::map<std::string, std::string> instance_id_data_; + // Factory for creating references when scheduling periodic checkin. base::WeakPtrFactory<GCMClientImpl> periodic_checkin_ptr_factory_; diff --git a/components/gcm_driver/gcm_driver.cc b/components/gcm_driver/gcm_driver.cc index 080ba52..1b115c0 100644 --- a/components/gcm_driver/gcm_driver.cc +++ b/components/gcm_driver/gcm_driver.cc @@ -12,6 +12,12 @@ namespace gcm { +InstanceIDStore::InstanceIDStore() { +} + +InstanceIDStore::~InstanceIDStore() { +} + GCMDriver::GCMDriver() : weak_ptr_factory_(this) { } diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h index fb20f3a..aac0975 100644 --- a/components/gcm_driver/gcm_driver.h +++ b/components/gcm_driver/gcm_driver.h @@ -22,6 +22,26 @@ class GCMAppHandler; class GCMConnectionObserver; struct AccountMapping; +// Provides the capability to set/get InstanceID data in the GCM store. +class InstanceIDStore { + public: + typedef base::Callback<void(const std::string& instance_id_data)> + GetInstanceIDDataCallback; + + InstanceIDStore(); + virtual ~InstanceIDStore(); + + virtual void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) = 0; + virtual void RemoveInstanceIDData(const std::string& app_id) = 0; + virtual void GetInstanceIDData( + const std::string& app_id, + const GetInstanceIDDataCallback& callback) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(InstanceIDStore); +}; + // Bridge between GCM users in Chrome and the platform-specific implementation. class GCMDriver { public: @@ -146,6 +166,9 @@ class GCMDriver { // to send a heartbeat message. virtual void WakeFromSuspendForHeartbeat(bool wake) = 0; + // Supports saving the Instance ID data in the GCM store. + virtual InstanceIDStore* GetInstanceIDStore() = 0; + protected: // Ensures that the GCM service starts (if necessary conditions are met). virtual GCMClient::Result EnsureStarted(GCMClient::StartMode start_mode) = 0; diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc index 2f82fbe..ec69952 100644 --- a/components/gcm_driver/gcm_driver_android.cc +++ b/components/gcm_driver/gcm_driver_android.cc @@ -162,6 +162,11 @@ void GCMDriverAndroid::SetLastTokenFetchTime(const base::Time& time) { void GCMDriverAndroid::WakeFromSuspendForHeartbeat(bool wake) { } +InstanceIDStore* GCMDriverAndroid::GetInstanceIDStore() { + // Not supported for Android. + return NULL; +} + GCMClient::Result GCMDriverAndroid::EnsureStarted( GCMClient::StartMode start_mode) { // TODO(johnme): Maybe we should check if GMS is available? diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h index 41c47b5..3c5d357 100644 --- a/components/gcm_driver/gcm_driver_android.h +++ b/components/gcm_driver/gcm_driver_android.h @@ -64,6 +64,7 @@ class GCMDriverAndroid : public GCMDriver { base::Time GetLastTokenFetchTime() override; void SetLastTokenFetchTime(const base::Time& time) override; void WakeFromSuspendForHeartbeat(bool wake) override; + InstanceIDStore* GetInstanceIDStore() override; protected: // GCMDriver implementation: diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc index 7db396d..a711999 100644 --- a/components/gcm_driver/gcm_driver_desktop.cc +++ b/components/gcm_driver/gcm_driver_desktop.cc @@ -87,6 +87,10 @@ class GCMDriverDesktop::IOWorker : public GCMClient::Delegate { void RemoveAccountMapping(const std::string& account_id); void SetLastTokenFetchTime(const base::Time& time); void WakeFromSuspendForHeartbeat(bool wake); + void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data); + void RemoveInstanceIDData(const std::string& app_id); + void GetInstanceIDData(const std::string& app_id); // For testing purpose. Can be called from UI thread. Use with care. GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); } @@ -343,6 +347,37 @@ void GCMDriverDesktop::IOWorker::SetLastTokenFetchTime(const base::Time& time) { gcm_client_->SetLastTokenFetchTime(time); } +void GCMDriverDesktop::IOWorker::AddInstanceIDData( + const std::string& app_id, + const std::string& instance_id_data) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + if (gcm_client_.get()) + gcm_client_->AddInstanceIDData(app_id, instance_id_data); +} + +void GCMDriverDesktop::IOWorker::RemoveInstanceIDData( + const std::string& app_id) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + if (gcm_client_.get()) + gcm_client_->RemoveInstanceIDData(app_id); +} + +void GCMDriverDesktop::IOWorker::GetInstanceIDData( + const std::string& app_id) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + std::string instance_id_data; + if (gcm_client_.get()) + instance_id_data = gcm_client_->GetInstanceIDData(app_id); + + ui_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::GetInstanceIDDataFinished, + service_, app_id, instance_id_data)); +} + void GCMDriverDesktop::IOWorker::WakeFromSuspendForHeartbeat(bool wake) { #if defined(OS_CHROMEOS) DCHECK(io_thread_->RunsTasksOnCurrentThread()); @@ -659,6 +694,53 @@ void GCMDriverDesktop::SetLastTokenFetchTime(const base::Time& time) { time)); } +InstanceIDStore* GCMDriverDesktop::GetInstanceIDStore() { + return this; +} + +void GCMDriverDesktop::AddInstanceIDData( + const std::string& app_id, + const std::string& instance_id_data) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + io_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::IOWorker::AddInstanceIDData, + base::Unretained(io_worker_.get()), + app_id, + instance_id_data)); +} + +void GCMDriverDesktop::RemoveInstanceIDData(const std::string& app_id) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + io_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::IOWorker::RemoveInstanceIDData, + base::Unretained(io_worker_.get()), + app_id)); +} + +void GCMDriverDesktop::GetInstanceIDData( + const std::string& app_id, + const GetInstanceIDDataCallback& callback) { + DCHECK(!get_instance_id_data_callbacks_.count(app_id)); + get_instance_id_data_callbacks_[app_id] = callback; + io_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::IOWorker::GetInstanceIDData, + base::Unretained(io_worker_.get()), + app_id)); +} + +void GCMDriverDesktop::GetInstanceIDDataFinished( + const std::string& app_id, + const std::string& instance_id_data) { + DCHECK(get_instance_id_data_callbacks_.count(app_id)); + get_instance_id_data_callbacks_[app_id].Run(instance_id_data); + get_instance_id_data_callbacks_.erase(app_id); +} + void GCMDriverDesktop::WakeFromSuspendForHeartbeat(bool wake) { DCHECK(ui_thread_->RunsTasksOnCurrentThread()); diff --git a/components/gcm_driver/gcm_driver_desktop.h b/components/gcm_driver/gcm_driver_desktop.h index 422f0f5..f52dd6bb 100644 --- a/components/gcm_driver/gcm_driver_desktop.h +++ b/components/gcm_driver/gcm_driver_desktop.h @@ -43,7 +43,8 @@ class GCMClientFactory; class GCMDelayedTaskController; // GCMDriver implementation for desktop and Chrome OS, using GCMClient. -class GCMDriverDesktop : public GCMDriver { +class GCMDriverDesktop : public GCMDriver, + public InstanceIDStore { public: GCMDriverDesktop( scoped_ptr<GCMClientFactory> gcm_client_factory, @@ -83,6 +84,14 @@ class GCMDriverDesktop : public GCMDriver { base::Time GetLastTokenFetchTime() override; void SetLastTokenFetchTime(const base::Time& time) override; void WakeFromSuspendForHeartbeat(bool wake) override; + InstanceIDStore* GetInstanceIDStore() override; + + // InstanceIDStore overrides: + void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) override; + void RemoveInstanceIDData(const std::string& app_id) override; + void GetInstanceIDData(const std::string& app_id, + const GetInstanceIDDataCallback& callback) override; // Exposed for testing purpose. bool gcm_enabled() const { return gcm_enabled_; } @@ -130,6 +139,8 @@ class GCMDriverDesktop : public GCMDriver { void OnDisconnected(); void GetGCMStatisticsFinished(const GCMClient::GCMStatistics& stats); + void GetInstanceIDDataFinished(const std::string& app_id, + const std::string& instance_id_data); scoped_ptr<GCMChannelStatusSyncer> gcm_channel_status_syncer_; @@ -172,6 +183,10 @@ class GCMDriverDesktop : public GCMDriver { // Callback for GetGCMStatistics. GetGCMStatisticsCallback request_gcm_statistics_callback_; + // Callbacks for GetInstanceIDData. + std::map<std::string, GetInstanceIDDataCallback> + get_instance_id_data_callbacks_; + // Used to pass a weak pointer to the IO worker. base::WeakPtrFactory<GCMDriverDesktop> weak_ptr_factory_; diff --git a/components/gcm_driver/instance_id/BUILD.gn b/components/gcm_driver/instance_id/BUILD.gn index 430de17..179828f 100644 --- a/components/gcm_driver/instance_id/BUILD.gn +++ b/components/gcm_driver/instance_id/BUILD.gn @@ -34,12 +34,13 @@ source_set("instance_id") { source_set("test_support") { testonly = true sources = [ - "fake_instance_id_driver.cc", - "fake_instance_id_driver.h", + "fake_gcm_driver_for_instance_id.cc", + "fake_gcm_driver_for_instance_id.h", ] deps = [ ":instance_id", + "//components/gcm_driver:test_support", "//testing/gtest", ] } diff --git a/components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.cc b/components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.cc new file mode 100644 index 0000000..2a50a03 --- /dev/null +++ b/components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.cc @@ -0,0 +1,45 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" + +namespace instance_id { + +FakeGCMDriverForInstanceID::FakeGCMDriverForInstanceID() { +} + +FakeGCMDriverForInstanceID::~FakeGCMDriverForInstanceID() { +} + +gcm::InstanceIDStore* FakeGCMDriverForInstanceID::GetInstanceIDStore() { + return this; +} + +void FakeGCMDriverForInstanceID::AddInstanceIDData( + const std::string& app_id, + const std::string& instance_id_data) { + instance_id_data_[app_id] = instance_id_data; +} + +void FakeGCMDriverForInstanceID::RemoveInstanceIDData( + const std::string& app_id) { + instance_id_data_.erase(app_id); +} + +void FakeGCMDriverForInstanceID::GetInstanceIDData( + const std::string& app_id, + const gcm::InstanceIDStore::GetInstanceIDDataCallback& callback) { + std::string data; + auto iter = instance_id_data_.find(app_id); + if (iter != instance_id_data_.end()) + data = iter->second; + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(callback, data)); +} + +} // namespace instance_id diff --git a/components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h b/components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h new file mode 100644 index 0000000..d5f9dd1 --- /dev/null +++ b/components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_GCM_DRIVER_INSTANCE_ID_FAKE_GCM_DRIVER__FOR_INSTANCE_ID_H_ +#define COMPONENTS_GCM_DRIVER_INSTANCE_ID_FAKE_GCM_DRIVER__FOR_INSTANCE_ID_H_ + +#include <map> +#include <string> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "components/gcm_driver/fake_gcm_driver.h" + +namespace instance_id { + +class FakeGCMDriverForInstanceID : public gcm::FakeGCMDriver, + public gcm::InstanceIDStore { + public: + FakeGCMDriverForInstanceID(); + ~FakeGCMDriverForInstanceID() override; + + // FakeGCMDriver overrides: + gcm::InstanceIDStore* GetInstanceIDStore() override; + + // InstanceIDStore overrides: + void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data) override; + void RemoveInstanceIDData(const std::string& app_id) override; + void GetInstanceIDData( + const std::string& app_id, + const gcm::InstanceIDStore::GetInstanceIDDataCallback& callback) override; + + private: + std::map<std::string, std::string> instance_id_data_; + + DISALLOW_COPY_AND_ASSIGN(FakeGCMDriverForInstanceID); +}; + +} // namespace instance_id + +#endif // COMPONENTS_GCM_DRIVER_INSTANCE_ID_FAKE_GCM_DRIVER__FOR_INSTANCE_ID_H_ diff --git a/components/gcm_driver/instance_id/fake_instance_id_driver.cc b/components/gcm_driver/instance_id/fake_instance_id_driver.cc deleted file mode 100644 index 6b88f22..0000000 --- a/components/gcm_driver/instance_id/fake_instance_id_driver.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/gcm_driver/instance_id/fake_instance_id_driver.h" - -namespace instance_id { - -FakeInstanceIDDriver::FakeInstanceIDDriver() - : InstanceIDDriver(NULL) { -} - -FakeInstanceIDDriver::~FakeInstanceIDDriver() { -} - -} // namespace instance_id diff --git a/components/gcm_driver/instance_id/fake_instance_id_driver.h b/components/gcm_driver/instance_id/fake_instance_id_driver.h deleted file mode 100644 index 60fc0b2..0000000 --- a/components/gcm_driver/instance_id/fake_instance_id_driver.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_GCM_DRIVER_INSTANCE_ID_FAKE_INSTANCE_ID_DRIVER_H_ -#define COMPONENTS_GCM_DRIVER_INSTANCE_ID_FAKE_INSTANCE_ID_DRIVER_H_ - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "components/gcm_driver/instance_id/instance_id_driver.h" - -namespace instance_id { - -class InstanceID; - -class FakeInstanceIDDriver : public InstanceIDDriver { - public: - FakeInstanceIDDriver(); - ~FakeInstanceIDDriver() override; - - private: - DISALLOW_COPY_AND_ASSIGN(FakeInstanceIDDriver); -}; - -} // namespace instance_id - -#endif // COMPONENTS_GCM_DRIVER_INSTANCE_ID_FAKE_INSTANCE_ID_DRIVER_H_ diff --git a/components/gcm_driver/instance_id/instance_id.h b/components/gcm_driver/instance_id/instance_id.h index 731d274..8141a3a 100644 --- a/components/gcm_driver/instance_id/instance_id.h +++ b/components/gcm_driver/instance_id/instance_id.h @@ -41,6 +41,9 @@ class InstanceID { // Asynchronous callbacks. typedef base::Callback<void(const std::string& app_id, bool update_id)> TokenRefreshCallback; + typedef base::Callback<void(const std::string& id)> GetIDCallback; + typedef base::Callback<void(const base::Time& creation_time)> + GetCreationTimeCallback; typedef base::Callback<void(const std::string& token, Result result)> GetTokenCallback; typedef base::Callback<void(Result result)> DeleteTokenCallback; @@ -62,10 +65,10 @@ class InstanceID { void SetTokenRefreshCallback(const TokenRefreshCallback& callback); // Returns the Instance ID. - virtual std::string GetID() = 0; + virtual void GetID(const GetIDCallback& callback) = 0; // Returns the time when the InstanceID has been generated. - virtual base::Time GetCreationTime() = 0; + virtual void GetCreationTime(const GetCreationTimeCallback& callback) = 0; // Retrieves a token that allows the authorized entity to access the service // defined as "scope". diff --git a/components/gcm_driver/instance_id/instance_id_android.cc b/components/gcm_driver/instance_id/instance_id_android.cc index 2057a814..9e7a164 100644 --- a/components/gcm_driver/instance_id/instance_id_android.cc +++ b/components/gcm_driver/instance_id/instance_id_android.cc @@ -21,14 +21,13 @@ InstanceIDAndroid::InstanceIDAndroid(const std::string& app_id) InstanceIDAndroid::~InstanceIDAndroid() { } -std::string InstanceIDAndroid::GetID() { +void InstanceIDAndroid::GetID(const GetIDCallback& callback) { NOTIMPLEMENTED(); - return std::string(); } -base::Time InstanceIDAndroid::GetCreationTime() { +void InstanceIDAndroid::GetCreationTime( + const GetCreationTimeCallback& callback) { NOTIMPLEMENTED(); - return base::Time(); } void InstanceIDAndroid::GetToken( diff --git a/components/gcm_driver/instance_id/instance_id_android.h b/components/gcm_driver/instance_id/instance_id_android.h index b09901a..7a6babf 100644 --- a/components/gcm_driver/instance_id/instance_id_android.h +++ b/components/gcm_driver/instance_id/instance_id_android.h @@ -19,12 +19,12 @@ namespace instance_id { // InstanceID implementation for Android. class InstanceIDAndroid : public InstanceID { public: - explicit InstanceIDAndroid(const std::string& app_id); + InstanceIDAndroid(const std::string& app_id); ~InstanceIDAndroid() override; // InstanceID: - std::string GetID() override; - base::Time GetCreationTime() override; + void GetID(const GetIDCallback& callback) override; + void GetCreationTime(const GetCreationTimeCallback& callback) override; void GetToken(const std::string& audience, const std::string& scope, const std::map<std::string, std::string>& options, diff --git a/components/gcm_driver/instance_id/instance_id_driver_unittest.cc b/components/gcm_driver/instance_id/instance_id_driver_unittest.cc index 37a1040..41305b5 100644 --- a/components/gcm_driver/instance_id/instance_id_driver_unittest.cc +++ b/components/gcm_driver/instance_id/instance_id_driver_unittest.cc @@ -9,7 +9,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/string_util.h" -#include "components/gcm_driver/fake_gcm_driver.h" +#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h" #include "components/gcm_driver/instance_id/instance_id.h" #include "testing/gtest/include/gtest/gtest.h" @@ -46,41 +46,107 @@ class InstanceIDDriverTest : public testing::Test { void WaitForAsyncOperation(); - void DeleteIDCompleted(InstanceID::Result result); + // Recreates InstanceIDDriver to simulate restart. + void RecreateInstanceIDDriver(); + + // Sync wrappers for async version. + std::string GetID(InstanceID* instance_id); + base::Time GetCreationTime(InstanceID* instance_id); + InstanceID::Result DeleteID(InstanceID* instance_id); InstanceIDDriver* driver() const { return driver_.get(); } - InstanceID::Result delete_id_result() const { return delete_id_result_; } private: + void GetIDCompleted(const std::string& id); + void GetCreationTimeCompleted(const base::Time& creation_time); + void DeleteIDCompleted(InstanceID::Result result); + base::MessageLoopForUI message_loop_; - scoped_ptr<gcm::FakeGCMDriver> gcm_driver_; + scoped_ptr<FakeGCMDriverForInstanceID> gcm_driver_; scoped_ptr<InstanceIDDriver> driver_; - InstanceID::Result delete_id_result_; + + std::string id_; + base::Time creation_time_; + InstanceID::Result result_; + + bool async_operation_completed_; base::Closure async_operation_completed_callback_; DISALLOW_COPY_AND_ASSIGN(InstanceIDDriverTest); }; InstanceIDDriverTest::InstanceIDDriverTest() - : delete_id_result_(InstanceID::UNKNOWN_ERROR) { + : result_(InstanceID::UNKNOWN_ERROR), + async_operation_completed_(false) { } InstanceIDDriverTest::~InstanceIDDriverTest() { } void InstanceIDDriverTest::SetUp() { - gcm_driver_.reset(new gcm::FakeGCMDriver); + gcm_driver_.reset(new FakeGCMDriverForInstanceID); + RecreateInstanceIDDriver(); +} + +void InstanceIDDriverTest::RecreateInstanceIDDriver() { driver_.reset(new InstanceIDDriver(gcm_driver_.get())); } void InstanceIDDriverTest::WaitForAsyncOperation() { + // No need to wait if async operation is not needed. + if (async_operation_completed_) + return; base::RunLoop run_loop; async_operation_completed_callback_ = run_loop.QuitClosure(); run_loop.Run(); } +std::string InstanceIDDriverTest::GetID(InstanceID* instance_id) { + async_operation_completed_ = false; + id_.clear(); + instance_id->GetID(base::Bind(&InstanceIDDriverTest::GetIDCompleted, + base::Unretained(this))); + WaitForAsyncOperation(); + return id_; +} + +base::Time InstanceIDDriverTest::GetCreationTime(InstanceID* instance_id) { + async_operation_completed_ = false; + creation_time_ = base::Time(); + instance_id->GetCreationTime( + base::Bind(&InstanceIDDriverTest::GetCreationTimeCompleted, + base::Unretained(this))); + WaitForAsyncOperation(); + return creation_time_; +} + +InstanceID::Result InstanceIDDriverTest::DeleteID(InstanceID* instance_id) { + async_operation_completed_ = false; + result_ = InstanceID::UNKNOWN_ERROR;; + instance_id->DeleteID(base::Bind(&InstanceIDDriverTest::DeleteIDCompleted, + base::Unretained(this))); + WaitForAsyncOperation(); + return result_; +} + +void InstanceIDDriverTest::GetIDCompleted(const std::string& id) { + async_operation_completed_ = true; + id_ = id; + if (!async_operation_completed_callback_.is_null()) + async_operation_completed_callback_.Run(); +} + +void InstanceIDDriverTest::GetCreationTimeCompleted( + const base::Time& creation_time) { + async_operation_completed_ = true; + creation_time_ = creation_time; + if (!async_operation_completed_callback_.is_null()) + async_operation_completed_callback_.Run(); +} + void InstanceIDDriverTest::DeleteIDCompleted(InstanceID::Result result) { - delete_id_result_ = result; + async_operation_completed_ = true; + result_ = result; if (!async_operation_completed_callback_.is_null()) async_operation_completed_callback_.Run(); } @@ -88,45 +154,70 @@ void InstanceIDDriverTest::DeleteIDCompleted(InstanceID::Result result) { TEST_F(InstanceIDDriverTest, NewID) { // Creation time should not be set when the ID is not created. InstanceID* instance_id1 = driver()->GetInstanceID(kTestAppID1); - EXPECT_TRUE(instance_id1->GetCreationTime().is_null()); + EXPECT_TRUE(GetCreationTime(instance_id1).is_null()); // New ID is generated for the first time. - std::string id1 = instance_id1->GetID(); - EXPECT_FALSE(id1.empty()); + std::string id1 = GetID(instance_id1); EXPECT_TRUE(VerifyInstanceID(id1)); - base::Time creation_time = instance_id1->GetCreationTime(); + base::Time creation_time = GetCreationTime(instance_id1); EXPECT_FALSE(creation_time.is_null()); // Same ID is returned for the same app. - EXPECT_EQ(id1, instance_id1->GetID()); - EXPECT_EQ(creation_time, instance_id1->GetCreationTime()); + EXPECT_EQ(id1, GetID(instance_id1)); + EXPECT_EQ(creation_time, GetCreationTime(instance_id1)); // New ID is generated for another app. InstanceID* instance_id2 = driver()->GetInstanceID(kTestAppID2); - std::string id2 = instance_id2->GetID(); - EXPECT_FALSE(id2.empty()); + std::string id2 = GetID(instance_id2); EXPECT_TRUE(VerifyInstanceID(id2)); EXPECT_NE(id1, id2); - EXPECT_FALSE(instance_id2->GetCreationTime().is_null()); + EXPECT_FALSE(GetCreationTime(instance_id2).is_null()); +} + +TEST_F(InstanceIDDriverTest, PersistID) { + InstanceID* instance_id = driver()->GetInstanceID(kTestAppID1); + + // Create the ID for the first time. The ID and creation time should be saved + // to the store. + std::string id = GetID(instance_id); + EXPECT_FALSE(id.empty()); + base::Time creation_time = GetCreationTime(instance_id); + EXPECT_FALSE(creation_time.is_null()); + + // Simulate restart by recreating InstanceIDDriver. Same ID and creation time + // should be expected. + RecreateInstanceIDDriver(); + instance_id = driver()->GetInstanceID(kTestAppID1); + EXPECT_EQ(creation_time, GetCreationTime(instance_id)); + EXPECT_EQ(id, GetID(instance_id)); + + // Delete the ID. The ID and creation time should be removed from the store. + EXPECT_EQ(InstanceID::SUCCESS, DeleteID(instance_id)); + EXPECT_TRUE(GetCreationTime(instance_id).is_null()); + + // Simulate restart by recreating InstanceIDDriver. Different ID should be + // expected. + // Note that we do not check for different creation time since the test might + // be run at a very fast server. + RecreateInstanceIDDriver(); + instance_id = driver()->GetInstanceID(kTestAppID1); + EXPECT_NE(id, GetID(instance_id)); } TEST_F(InstanceIDDriverTest, DeleteID) { InstanceID* instance_id = driver()->GetInstanceID(kTestAppID1); - std::string id1 = instance_id->GetID(); + std::string id1 = GetID(instance_id); EXPECT_FALSE(id1.empty()); - EXPECT_FALSE(instance_id->GetCreationTime().is_null()); + EXPECT_FALSE(GetCreationTime(instance_id).is_null()); // New ID will be generated from GetID after calling DeleteID. - instance_id->DeleteID(base::Bind(&InstanceIDDriverTest::DeleteIDCompleted, - base::Unretained(this))); - WaitForAsyncOperation(); - EXPECT_EQ(InstanceID::SUCCESS, delete_id_result()); - EXPECT_TRUE(instance_id->GetCreationTime().is_null()); + EXPECT_EQ(InstanceID::SUCCESS, DeleteID(instance_id)); + EXPECT_TRUE(GetCreationTime(instance_id).is_null()); - std::string id2 = instance_id->GetID(); + std::string id2 = GetID(instance_id); EXPECT_FALSE(id2.empty()); EXPECT_NE(id1, id2); - EXPECT_FALSE(instance_id->GetCreationTime().is_null()); + EXPECT_FALSE(GetCreationTime(instance_id).is_null()); } } // instance_id diff --git a/components/gcm_driver/instance_id/instance_id_impl.cc b/components/gcm_driver/instance_id/instance_id_impl.cc index 0ebcdf8..b77cf3f 100644 --- a/components/gcm_driver/instance_id/instance_id_impl.cc +++ b/components/gcm_driver/instance_id/instance_id_impl.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" +#include "base/strings/string_number_conversions.h" #include "components/gcm_driver/gcm_driver_desktop.h" #include "crypto/random.h" @@ -23,19 +24,50 @@ InstanceID* InstanceID::Create(const std::string& app_id, InstanceIDImpl::InstanceIDImpl(const std::string& app_id, gcm::GCMDriver* gcm_driver) : InstanceID(app_id), - gcm_driver_(gcm_driver) { + gcm_driver_(gcm_driver), + load_from_store_(false), + weak_ptr_factory_(this) { + gcm_driver_->GetInstanceIDStore()->GetInstanceIDData( + app_id, + base::Bind(&InstanceIDImpl::GetInstanceIDDataCompleted, + weak_ptr_factory_.GetWeakPtr())); } InstanceIDImpl::~InstanceIDImpl() { } -std::string InstanceIDImpl::GetID() { +void InstanceIDImpl::GetID(const GetIDCallback& callback) { + if (!delayed_task_controller_.CanRunTaskWithoutDelay()) { + delayed_task_controller_.AddTask( + base::Bind(&InstanceIDImpl::DoGetID, + weak_ptr_factory_.GetWeakPtr(), + callback)); + return; + } + + DoGetID(callback); +} + +void InstanceIDImpl::DoGetID(const GetIDCallback& callback) { EnsureIDGenerated(); - return id_; + callback.Run(id_); +} + +void InstanceIDImpl::GetCreationTime(const GetCreationTimeCallback& callback) { + if (!delayed_task_controller_.CanRunTaskWithoutDelay()) { + delayed_task_controller_.AddTask( + base::Bind(&InstanceIDImpl::DoGetCreationTime, + weak_ptr_factory_.GetWeakPtr(), + callback)); + return; + } + + DoGetCreationTime(callback); } -base::Time InstanceIDImpl::GetCreationTime() { - return creation_time_; +void InstanceIDImpl::DoGetCreationTime( + const GetCreationTimeCallback& callback) { + callback.Run(creation_time_); } void InstanceIDImpl::GetToken( @@ -53,7 +85,8 @@ void InstanceIDImpl::DeleteToken(const std::string& authorized_entity, } void InstanceIDImpl::DeleteID(const DeleteIDCallback& callback) { - // TODO(jianli): Delete the ID from the store. + gcm_driver_->GetInstanceIDStore()->RemoveInstanceIDData(app_id()); + id_.clear(); creation_time_ = base::Time(); @@ -62,6 +95,12 @@ void InstanceIDImpl::DeleteID(const DeleteIDCallback& callback) { base::Bind(callback, InstanceID::SUCCESS)); } +void InstanceIDImpl::GetInstanceIDDataCompleted( + const std::string& instance_id_data) { + Deserialize(instance_id_data); + delayed_task_controller_.SetReady(); +} + void InstanceIDImpl::EnsureIDGenerated() { if (!id_.empty()) return; @@ -91,7 +130,36 @@ void InstanceIDImpl::EnsureIDGenerated() { creation_time_ = base::Time::Now(); - // TODO(jianli): Save the ID to the store. + // Save to the persistent store. + gcm_driver_->GetInstanceIDStore()->AddInstanceIDData( + app_id(), SerializeAsString()); +} + +std::string InstanceIDImpl::SerializeAsString() const { + std::string serialized_data; + serialized_data += id_; + serialized_data += ","; + serialized_data += base::Int64ToString(creation_time_.ToInternalValue()); + return serialized_data; +} + +void InstanceIDImpl::Deserialize(const std::string& serialized_data) { + if (serialized_data.empty()) + return; + std::size_t pos = serialized_data.find(','); + if (pos == std::string::npos) { + DVLOG(1) << "Failed to deserialize the InstanceID data: " + serialized_data; + return; + } + + id_ = serialized_data.substr(0, pos); + + int64 time_internal = 0LL; + if (!base::StringToInt64(serialized_data.substr(pos + 1), &time_internal)) { + DVLOG(1) << "Failed to deserialize the InstanceID data: " + serialized_data; + return; + } + creation_time_ = base::Time::FromInternalValue(time_internal); } } // namespace instance_id diff --git a/components/gcm_driver/instance_id/instance_id_impl.h b/components/gcm_driver/instance_id/instance_id_impl.h index dd7b775..a5124c1 100644 --- a/components/gcm_driver/instance_id/instance_id_impl.h +++ b/components/gcm_driver/instance_id/instance_id_impl.h @@ -11,7 +11,9 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/time/time.h" +#include "components/gcm_driver/gcm_delayed_task_controller.h" #include "components/gcm_driver/instance_id/instance_id.h" namespace gcm { @@ -27,8 +29,8 @@ class InstanceIDImpl : public InstanceID { ~InstanceIDImpl() override; // InstanceID: - std::string GetID() override; - base::Time GetCreationTime() override; + void GetID(const GetIDCallback& callback) override; + void GetCreationTime(const GetCreationTimeCallback& callback) override; void GetToken(const std::string& authorized_entity, const std::string& scope, const std::map<std::string, std::string>& options, @@ -39,9 +41,22 @@ class InstanceIDImpl : public InstanceID { void DeleteID(const DeleteIDCallback& callback) override; private: + void EnsureIDGenerated(); + void GetInstanceIDDataCompleted(const std::string& instance_id_data); + + void DoGetID(const GetIDCallback& callback); + void DoGetCreationTime(const GetCreationTimeCallback& callback); + + // Encodes/decodes the InstanceID data to work with the persistent store. + std::string SerializeAsString() const; + void Deserialize(const std::string& serialized_data); + gcm::GCMDriver* gcm_driver_; // Not owned. - void EnsureIDGenerated(); + gcm::GCMDelayedTaskController delayed_task_controller_; + + // Flag to indicate that we have tries to load the data from the store. + bool load_from_store_; // The generated Instance ID. std::string id_; @@ -49,6 +64,8 @@ class InstanceIDImpl : public InstanceID { // The time when the Instance ID has been generated. base::Time creation_time_; + base::WeakPtrFactory<InstanceIDImpl> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(InstanceIDImpl); }; diff --git a/google_apis/gcm/engine/gcm_store.cc b/google_apis/gcm/engine/gcm_store.cc index 31f2992..6f5ab0e 100644 --- a/google_apis/gcm/engine/gcm_store.cc +++ b/google_apis/gcm/engine/gcm_store.cc @@ -27,6 +27,7 @@ void GCMStore::LoadResult::Reset() { last_checkin_accounts.clear(); account_mappings.clear(); success = false; + instance_id_data.clear(); } GCMStore::GCMStore() {} diff --git a/google_apis/gcm/engine/gcm_store.h b/google_apis/gcm/engine/gcm_store.h index 0cbb085..cfefe91 100644 --- a/google_apis/gcm/engine/gcm_store.h +++ b/google_apis/gcm/engine/gcm_store.h @@ -57,6 +57,7 @@ class GCM_EXPORT GCMStore { AccountMappings account_mappings; base::Time last_token_fetch_time; std::map<std::string, int> heartbeat_intervals; + std::map<std::string, std::string> instance_id_data; }; typedef std::vector<std::string> PersistentIdList; @@ -141,6 +142,13 @@ class GCM_EXPORT GCMStore { virtual void RemoveHeartbeatInterval(const std::string& scope, const UpdateCallback& callback) = 0; + // Instance ID data. + virtual void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data, + const UpdateCallback& callback) = 0; + virtual void RemoveInstanceIDData(const std::string& app_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 72220dc..09bfb83 100644 --- a/google_apis/gcm/engine/gcm_store_impl.cc +++ b/google_apis/gcm/engine/gcm_store_impl.cc @@ -48,6 +48,7 @@ enum LoadStatus { LOADING_ACCOUNT_MAPPING_FAILED, LOADING_LAST_TOKEN_TIME_FAILED, LOADING_HEARTBEAT_INTERVALS_FAILED, + LOADING_INSTANCE_ID_DATA_FAILED, // NOTE: always keep this entry at the end. Add new status types only // immediately above this line. Make sure to update the corresponding @@ -107,6 +108,12 @@ const char kHeartbeatKeyStart[] = "heartbeat1-"; const char kHeartbeatKeyEnd[] = "heartbeat2-"; // Key used for last token fetch time. const char kLastTokenFetchTimeKey[] = "last_token_fetch_time"; +// Lowest lexicographically ordered app ids. +// Used for prefixing app id. +const char kInstanceIDKeyStart[] = "iid1-"; +// Key guaranteed to be higher than all app ids. +// Used for limiting iteration. +const char kInstanceIDKeyEnd[] = "iid2-"; std::string MakeRegistrationKey(const std::string& app_id) { return kRegistrationKeyStart + app_id; @@ -152,6 +159,14 @@ std::string ParseHeartbeatKey(const std::string& key) { return key.substr(arraysize(kHeartbeatKeyStart) - 1); } +std::string MakeInstanceIDKey(const std::string& app_id) { + return kInstanceIDKeyStart + app_id; +} + +std::string ParseInstanceIDKey(const std::string& key) { + return key.substr(arraysize(kInstanceIDKeyStart) - 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. @@ -214,6 +229,11 @@ class GCMStoreImpl::Backend const UpdateCallback& callback); void RemoveHeartbeatInterval(const std::string& scope, const UpdateCallback& callback); + void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data, + const UpdateCallback& callback); + void RemoveInstanceIDData(const std::string& app_id, + const UpdateCallback& callback); void SetValue(const std::string& key, const std::string& value, const UpdateCallback& callback); @@ -234,6 +254,7 @@ class GCMStoreImpl::Backend bool LoadAccountMappingInfo(AccountMappings* account_mappings); bool LoadLastTokenFetchTime(base::Time* last_token_fetch_time); bool LoadHeartbeatIntervals(std::map<std::string, int>* heartbeat_intervals); + bool LoadInstanceIDData(std::map<std::string, std::string>* instance_id_data); const base::FilePath path_; scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_; @@ -297,6 +318,8 @@ LoadStatus GCMStoreImpl::Backend::OpenStoreAndLoadData(LoadResult* result) { return LOADING_LAST_TOKEN_TIME_FAILED; if (!LoadHeartbeatIntervals(&result->heartbeat_intervals)) return LOADING_HEARTBEAT_INTERVALS_FAILED; + if (!LoadInstanceIDData(&result->instance_id_data)) + return LOADING_INSTANCE_ID_DATA_FAILED; return LOADING_SUCCEEDED; } @@ -607,6 +630,28 @@ void GCMStoreImpl::Backend::SetLastCheckinInfo( foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok())); } +void GCMStoreImpl::AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data, + const UpdateCallback& callback) { + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&GCMStoreImpl::Backend::AddInstanceIDData, + backend_, + app_id, + instance_id_data, + callback)); +} + +void GCMStoreImpl::RemoveInstanceIDData(const std::string& app_id, + const UpdateCallback& callback) { + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&GCMStoreImpl::Backend::RemoveInstanceIDData, + backend_, + app_id, + callback)); +} + void GCMStoreImpl::Backend::SetGServicesSettings( const std::map<std::string, std::string>& settings, const std::string& settings_digest, @@ -757,6 +802,49 @@ void GCMStoreImpl::Backend::RemoveHeartbeatInterval( foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok())); } +void GCMStoreImpl::Backend::AddInstanceIDData( + const std::string& app_id, + const std::string& instance_id_data, + const UpdateCallback& callback) { + DVLOG(1) << "Adding Instance ID data."; + 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 key = MakeInstanceIDKey(app_id); + const leveldb::Status status = db_->Put(write_options, + MakeSlice(key), + MakeSlice(instance_id_data)); + if (!status.ok()) + LOG(ERROR) << "LevelDB put failed: " << status.ToString(); + foreground_task_runner_->PostTask( + FROM_HERE, base::Bind(callback, status.ok())); +} + +void GCMStoreImpl::Backend::RemoveInstanceIDData( + const std::string& app_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 status = + db_->Delete(write_options, MakeSlice(MakeInstanceIDKey(app_id))); + if (!status.ok()) + LOG(ERROR) << "LevelDB remove failed: " << status.ToString(); + foreground_task_runner_->PostTask( + FROM_HERE, base::Bind(callback, status.ok())); +} + void GCMStoreImpl::Backend::SetValue(const std::string& key, const std::string& value, const UpdateCallback& callback) { @@ -1022,6 +1110,28 @@ bool GCMStoreImpl::Backend::LoadHeartbeatIntervals( return true; } +bool GCMStoreImpl::Backend::LoadInstanceIDData( + std::map<std::string, std::string>* instance_id_data) { + leveldb::ReadOptions read_options; + read_options.verify_checksums = true; + + scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options)); + for (iter->Seek(MakeSlice(kInstanceIDKeyStart)); + iter->Valid() && iter->key().ToString() < kInstanceIDKeyEnd; + iter->Next()) { + leveldb::Slice s = iter->value(); + if (s.size() <= 1) { + LOG(ERROR) << "Error reading IID data with key " << s.ToString(); + return false; + } + std::string app_id = ParseInstanceIDKey(iter->key().ToString()); + DVLOG(1) << "Found IID data with app id " << app_id; + (*instance_id_data)[app_id] = s.ToString(); + } + + return true; +} + GCMStoreImpl::GCMStoreImpl( const base::FilePath& path, scoped_refptr<base::SequencedTaskRunner> blocking_task_runner, diff --git a/google_apis/gcm/engine/gcm_store_impl.h b/google_apis/gcm/engine/gcm_store_impl.h index 6ee8e88..4815b20 100644 --- a/google_apis/gcm/engine/gcm_store_impl.h +++ b/google_apis/gcm/engine/gcm_store_impl.h @@ -102,6 +102,13 @@ class GCM_EXPORT GCMStoreImpl : public GCMStore { void RemoveHeartbeatInterval(const std::string& scope, const UpdateCallback& callback) override; + // Instance ID data. + void AddInstanceIDData(const std::string& app_id, + const std::string& instance_id_data, + const UpdateCallback& callback) override; + void RemoveInstanceIDData(const std::string& app_id, + const UpdateCallback& callback) override; + // Injects a value to database. Only to be used for testing. void SetValueForTesting(const std::string& key, const std::string& value, diff --git a/google_apis/gcm/engine/gcm_store_impl_unittest.cc b/google_apis/gcm/engine/gcm_store_impl_unittest.cc index 1a6c4bb..b7bf75d 100644 --- a/google_apis/gcm/engine/gcm_store_impl_unittest.cc +++ b/google_apis/gcm/engine/gcm_store_impl_unittest.cc @@ -33,6 +33,7 @@ const int kNumMessagesPerApp = 20; // App name for testing. const char kAppName[] = "my_app"; +const char kAppName2[] = "my_app_2"; // Category name for testing. const char kCategoryName[] = "my_category"; @@ -741,6 +742,56 @@ TEST_F(GCMStoreImplTest, LastTokenFetchTime) { EXPECT_EQ(base::Time(), load_result->last_token_fetch_time); } +TEST_F(GCMStoreImplTest, InstanceIDData) { + 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::string instance_id_data("Foo"); + gcm_store->AddInstanceIDData( + kAppName, + instance_id_data, + base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); + PumpLoop(); + + std::string instance_id_data2("Hello Instance ID"); + gcm_store->AddInstanceIDData( + kAppName2, + instance_id_data2, + 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(2u, load_result->instance_id_data.size()); + ASSERT_TRUE(load_result->instance_id_data.find(kAppName) != + load_result->instance_id_data.end()); + ASSERT_TRUE(load_result->instance_id_data.find(kAppName2) != + load_result->instance_id_data.end()); + EXPECT_EQ(instance_id_data, load_result->instance_id_data[kAppName]); + EXPECT_EQ(instance_id_data2, load_result->instance_id_data[kAppName2]); + + gcm_store->RemoveInstanceIDData( + kAppName, + 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(1u, load_result->instance_id_data.size()); + ASSERT_TRUE(load_result->instance_id_data.find(kAppName2) != + load_result->instance_id_data.end()); + EXPECT_EQ(instance_id_data2, load_result->instance_id_data[kAppName2]); +} + } // namespace } // namespace gcm diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 76146ce..ef1870e 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -53963,6 +53963,7 @@ Therefore, the affected-histogram name has to have at least one dot in it. <int value="9" label="Loading account mapping failed"/> <int value="10" label="Loading last token time failed"/> <int value="11" label="Loading heartbeat intervals failed"/> + <int value="12" label="Loading Instance ID data failed"/> </enum> <enum name="GCMOutgoingMessageTTLCategory" type="int"> |