Commit cdc4d73b authored by Tanja Gornak's avatar Tanja Gornak Committed by Commit Bot

Invalidations. Re-request instance id token and notify on change.

The InsatnceId token can become invalid. This CL re-requests the token
once a day and notifies the listener (FCMSyncInvalidationLIstener) in case token
has become invalid.

This CL only passes new token to the appropriate listener, but doesn't act on the change.


Bug: 801985
Change-Id: I004983225e22326c6e06cc3b97098d7784d07754
Reviewed-on: https://chromium-review.googlesource.com/1166967
Commit-Queue: Tatiana Gornak <melandory@chromium.org>
Reviewed-by: default avatarPavel Yatsuk <pavely@chromium.org>
Reviewed-by: default avatarJan Krcal <jkrcal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585791}
parent 598f81e2
...@@ -29,6 +29,9 @@ const char kContentKey[] = "data"; ...@@ -29,6 +29,9 @@ const char kContentKey[] = "data";
// Must match Java GoogleCloudMessaging.INSTANCE_ID_SCOPE. // Must match Java GoogleCloudMessaging.INSTANCE_ID_SCOPE.
const char kGCMScope[] = "GCM"; const char kGCMScope[] = "GCM";
// Lower bound time between two token validations when listening.
const int kTokenValidationPeriodMinutesDefault = 60 * 24;
} // namespace } // namespace
FCMNetworkHandler::FCMNetworkHandler( FCMNetworkHandler::FCMNetworkHandler(
...@@ -36,6 +39,7 @@ FCMNetworkHandler::FCMNetworkHandler( ...@@ -36,6 +39,7 @@ FCMNetworkHandler::FCMNetworkHandler(
instance_id::InstanceIDDriver* instance_id_driver) instance_id::InstanceIDDriver* instance_id_driver)
: gcm_driver_(gcm_driver), : gcm_driver_(gcm_driver),
instance_id_driver_(instance_id_driver), instance_id_driver_(instance_id_driver),
token_validation_timer_(std::make_unique<base::OneShotTimer>()),
weak_ptr_factory_(this) {} weak_ptr_factory_(this) {}
FCMNetworkHandler::~FCMNetworkHandler() { FCMNetworkHandler::~FCMNetworkHandler() {
...@@ -43,26 +47,35 @@ FCMNetworkHandler::~FCMNetworkHandler() { ...@@ -43,26 +47,35 @@ FCMNetworkHandler::~FCMNetworkHandler() {
} }
void FCMNetworkHandler::StartListening() { void FCMNetworkHandler::StartListening() {
// Adding ourselves as Handler means start listening.
// Being the listener is pre-requirement for token operations.
gcm_driver_->AddAppHandler(kInvalidationsAppId, this);
instance_id_driver_->GetInstanceID(kInvalidationsAppId) instance_id_driver_->GetInstanceID(kInvalidationsAppId)
->GetToken(kInvalidationGCMSenderId, kGCMScope, ->GetToken(kInvalidationGCMSenderId, kGCMScope,
/*options=*/std::map<std::string, std::string>(), /*options=*/std::map<std::string, std::string>(),
base::BindRepeating(&FCMNetworkHandler::DidRetrieveToken, base::BindRepeating(&FCMNetworkHandler::DidRetrieveToken,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
gcm_driver_->AddAppHandler(kInvalidationsAppId, this);
} }
void FCMNetworkHandler::StopListening() { void FCMNetworkHandler::StopListening() {
if (gcm_driver_->GetAppHandler(kInvalidationsAppId)) if (IsListening())
gcm_driver_->RemoveAppHandler(kInvalidationsAppId); gcm_driver_->RemoveAppHandler(kInvalidationsAppId);
} }
bool FCMNetworkHandler::IsListening() const {
return gcm_driver_->GetAppHandler(kInvalidationsAppId);
}
void FCMNetworkHandler::DidRetrieveToken(const std::string& subscription_token, void FCMNetworkHandler::DidRetrieveToken(const std::string& subscription_token,
InstanceID::Result result) { InstanceID::Result result) {
switch (result) { switch (result) {
case InstanceID::SUCCESS: case InstanceID::SUCCESS:
// The received token is assumed to be valid, therefore, we reschedule
// validation.
DeliverToken(subscription_token); DeliverToken(subscription_token);
return; token_ = subscription_token;
break;
case InstanceID::INVALID_PARAMETER: case InstanceID::INVALID_PARAMETER:
case InstanceID::DISABLED: case InstanceID::DISABLED:
case InstanceID::ASYNC_OPERATION_PENDING: case InstanceID::ASYNC_OPERATION_PENDING:
...@@ -74,6 +87,46 @@ void FCMNetworkHandler::DidRetrieveToken(const std::string& subscription_token, ...@@ -74,6 +87,46 @@ void FCMNetworkHandler::DidRetrieveToken(const std::string& subscription_token,
UpdateGcmChannelState(false); UpdateGcmChannelState(false);
break; break;
} }
ScheduleNextTokenValidation();
}
void FCMNetworkHandler::ScheduleNextTokenValidation() {
DCHECK(IsListening());
token_validation_timer_->Start(
FROM_HERE,
base::TimeDelta::FromMinutes(kTokenValidationPeriodMinutesDefault),
base::BindOnce(&FCMNetworkHandler::StartTokenValidation,
weak_ptr_factory_.GetWeakPtr()));
}
void FCMNetworkHandler::StartTokenValidation() {
DCHECK(IsListening());
instance_id_driver_->GetInstanceID(kInvalidationsAppId)
->GetToken(kInvalidationGCMSenderId, kGCMScope,
std::map<std::string, std::string>(),
base::Bind(&FCMNetworkHandler::DidReceiveTokenForValidation,
weak_ptr_factory_.GetWeakPtr()));
}
void FCMNetworkHandler::DidReceiveTokenForValidation(
const std::string& new_token,
InstanceID::Result result) {
if (!IsListening()) {
// After we requested the token, |StopListening| has been called. Thus,
// ignore the token.
return;
}
if (result == InstanceID::SUCCESS) {
if (token_ != new_token) {
token_ = new_token;
DeliverToken(new_token);
}
}
ScheduleNextTokenValidation();
} }
void FCMNetworkHandler::UpdateGcmChannelState(bool online) { void FCMNetworkHandler::UpdateGcmChannelState(bool online) {
...@@ -129,4 +182,9 @@ void FCMNetworkHandler::OnSendAcknowledged(const std::string& app_id, ...@@ -129,4 +182,9 @@ void FCMNetworkHandler::OnSendAcknowledged(const std::string& app_id,
NOTREACHED() << "FCMNetworkHandler doesn't send GCM messages."; NOTREACHED() << "FCMNetworkHandler doesn't send GCM messages.";
} }
void FCMNetworkHandler::SetTokenValidationTimerForTesting(
std::unique_ptr<base::OneShotTimer> token_validation_timer) {
token_validation_timer_ = std::move(token_validation_timer);
}
} // namespace syncer } // namespace syncer
...@@ -6,9 +6,13 @@ ...@@ -6,9 +6,13 @@
#define COMPONENTS_INVALIDATION_IMPL_FCM_NETWORK_HANDLER_H_ #define COMPONENTS_INVALIDATION_IMPL_FCM_NETWORK_HANDLER_H_
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/time/clock.h"
#include "base/timer/timer.h"
#include "components/gcm_driver/gcm_app_handler.h" #include "components/gcm_driver/gcm_app_handler.h"
#include "components/gcm_driver/instance_id/instance_id.h" #include "components/gcm_driver/instance_id/instance_id.h"
#include "components/invalidation/impl/fcm_sync_network_channel.h" #include "components/invalidation/impl/fcm_sync_network_channel.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace gcm { namespace gcm {
class GCMDriver; class GCMDriver;
...@@ -38,6 +42,7 @@ class FCMNetworkHandler : public gcm::GCMAppHandler, ...@@ -38,6 +42,7 @@ class FCMNetworkHandler : public gcm::GCMAppHandler,
void StartListening(); void StartListening();
void StopListening(); void StopListening();
bool IsListening() const;
void UpdateGcmChannelState(bool); void UpdateGcmChannelState(bool);
// GCMAppHandler overrides. // GCMAppHandler overrides.
...@@ -51,16 +56,25 @@ class FCMNetworkHandler : public gcm::GCMAppHandler, ...@@ -51,16 +56,25 @@ class FCMNetworkHandler : public gcm::GCMAppHandler,
void OnSendAcknowledged(const std::string& app_id, void OnSendAcknowledged(const std::string& app_id,
const std::string& message_id) override; const std::string& message_id) override;
void SetTokenValidationTimerForTesting(
std::unique_ptr<base::OneShotTimer> token_validation_timer);
private: private:
// Called when a subscription token is obtained from the GCM server. // Called when a subscription token is obtained from the GCM server.
void DidRetrieveToken(const std::string& subscription_token, void DidRetrieveToken(const std::string& subscription_token,
instance_id::InstanceID::Result result); instance_id::InstanceID::Result result);
void ScheduleNextTokenValidation();
void StartTokenValidation();
void DidReceiveTokenForValidation(const std::string& new_token,
instance_id::InstanceID::Result result);
gcm::GCMDriver* const gcm_driver_; gcm::GCMDriver* const gcm_driver_;
instance_id::InstanceIDDriver* const instance_id_driver_; instance_id::InstanceIDDriver* const instance_id_driver_;
bool gcm_channel_online_ = false; bool gcm_channel_online_ = false;
std::string token_;
std::unique_ptr<base::OneShotTimer> token_validation_timer_;
base::WeakPtrFactory<FCMNetworkHandler> weak_ptr_factory_; base::WeakPtrFactory<FCMNetworkHandler> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(FCMNetworkHandler); DISALLOW_COPY_AND_ASSIGN(FCMNetworkHandler);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/test/mock_callback.h" #include "base/test/mock_callback.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/gcm_driver/gcm_driver.h" #include "components/gcm_driver/gcm_driver.h"
#include "components/gcm_driver/instance_id/instance_id.h" #include "components/gcm_driver/instance_id/instance_id.h"
...@@ -18,6 +19,7 @@ ...@@ -18,6 +19,7 @@
#include "google_apis/gcm/engine/account_mapping.h" #include "google_apis/gcm/engine/account_mapping.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
using base::TestMockTimeTaskRunner;
using gcm::InstanceIDHandler; using gcm::InstanceIDHandler;
using instance_id::InstanceID; using instance_id::InstanceID;
using instance_id::InstanceIDDriver; using instance_id::InstanceIDDriver;
...@@ -31,6 +33,12 @@ namespace { ...@@ -31,6 +33,12 @@ namespace {
const char kInvalidationsAppId[] = "com.google.chrome.fcm.invalidations"; const char kInvalidationsAppId[] = "com.google.chrome.fcm.invalidations";
using DataCallback = base::RepeatingCallback<void(const std::string& message)>; using DataCallback = base::RepeatingCallback<void(const std::string& message)>;
base::Time GetDummyNow() {
base::Time out_time;
EXPECT_TRUE(base::Time::FromUTCString("2017-01-02T00:00:01Z", &out_time));
return out_time;
}
class MockInstanceID : public InstanceID { class MockInstanceID : public InstanceID {
public: public:
MockInstanceID() : InstanceID("app_id", /*gcm_driver=*/nullptr) {} MockInstanceID() : InstanceID("app_id", /*gcm_driver=*/nullptr) {}
...@@ -185,6 +193,19 @@ class FCMNetworkHandlerTest : public testing::Test { ...@@ -185,6 +193,19 @@ class FCMNetworkHandlerTest : public testing::Test {
return mock_instance_id_.get(); return mock_instance_id_.get();
} }
scoped_refptr<TestMockTimeTaskRunner> CreateFakeTaskRunnerAndInjectToHandler(
std::unique_ptr<FCMNetworkHandler>& handler) {
scoped_refptr<TestMockTimeTaskRunner> task_runner(
new TestMockTimeTaskRunner(GetDummyNow(), base::TimeTicks::Now()));
auto token_validation_timer =
std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock());
token_validation_timer->SetTaskRunner(task_runner);
handler->SetTokenValidationTimerForTesting(
std::move(token_validation_timer));
return task_runner;
}
private: private:
base::MessageLoop message_loop_; base::MessageLoop message_loop_;
std::unique_ptr<StrictMock<MockGCMDriver>> mock_gcm_driver_; std::unique_ptr<StrictMock<MockGCMDriver>> mock_gcm_driver_;
...@@ -251,4 +272,97 @@ TEST_F(FCMNetworkHandlerTest, ShouldInvokeMessageCallbackOnValidMessage) { ...@@ -251,4 +272,97 @@ TEST_F(FCMNetworkHandlerTest, ShouldInvokeMessageCallbackOnValidMessage) {
handler->OnMessage(kInvalidationsAppId, gcm::IncomingMessage()); handler->OnMessage(kInvalidationsAppId, gcm::IncomingMessage());
} }
TEST_F(FCMNetworkHandlerTest, ShouldRequestTokenImmediatellyEvenIfSaved) {
// Setting up network handler.
MockOnDataCallback mock_on_token_callback;
auto handler = MakeHandler();
auto task_runner = CreateFakeTaskRunnerAndInjectToHandler(handler);
handler->SetTokenReceiver(mock_on_token_callback.Get());
// Check that after StartListening we receive the token and store it.
EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
.WillOnce(
InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
EXPECT_CALL(mock_on_token_callback, Run("token")).Times(1);
handler->StartListening();
handler->StopListening();
task_runner->RunUntilIdle();
// Setting up another network handler.
auto handler2 = MakeHandler();
auto task_runner2 = CreateFakeTaskRunnerAndInjectToHandler(handler2);
handler2->SetTokenReceiver(mock_on_token_callback.Get());
// Check that after StartListening the token will be requested, depite we have
// saved token.
EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
.WillOnce(
InvokeCallbackArgument<3>("token_new", InstanceID::Result::SUCCESS));
EXPECT_CALL(mock_on_token_callback, Run("token_new")).Times(1);
handler->StartListening();
task_runner->RunUntilIdle();
}
TEST_F(FCMNetworkHandlerTest, ShouldScheduleTokenValidationAndActOnNewToken) {
// Setting up network handler.
MockOnDataCallback mock_on_token_callback;
auto handler = MakeHandler();
auto task_runner = CreateFakeTaskRunnerAndInjectToHandler(handler);
handler->SetTokenReceiver(mock_on_token_callback.Get());
// Checking that after start listening the token will be requested
// and passed to the appropriate token receiver.
EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
.WillOnce(
InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0);
EXPECT_CALL(mock_on_token_callback, Run("token")).Times(1);
handler->StartListening();
testing::Mock::VerifyAndClearExpectations(mock_instance_id());
// Adjust timers and check that validation will happen in time.
// The old token was invalid, so token listener shold be informed.
const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(24);
task_runner->FastForwardBy(time_to_validation -
base::TimeDelta::FromSeconds(1));
// But when it is time, validation happens.
EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
.WillOnce(
InvokeCallbackArgument<3>("token_new", InstanceID::Result::SUCCESS));
EXPECT_CALL(mock_on_token_callback, Run("token_new")).Times(1);
task_runner->FastForwardBy(base::TimeDelta::FromSeconds(1));
}
TEST_F(FCMNetworkHandlerTest,
ShouldScheduleTokenValidationAndDoNotActOnSameToken) {
// Setting up network handler.
MockOnDataCallback mock_on_token_callback;
std::unique_ptr<FCMNetworkHandler> handler = MakeHandler();
auto task_runner = CreateFakeTaskRunnerAndInjectToHandler(handler);
handler->SetTokenReceiver(mock_on_token_callback.Get());
// Checking that after start listening the token will be requested
// and passed to the appropriate token receiver
EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
.WillOnce(
InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0);
EXPECT_CALL(mock_on_token_callback, Run("token")).Times(1);
handler->StartListening();
testing::Mock::VerifyAndClearExpectations(mock_instance_id());
// Adjust timers and check that validation will happen in time.
// The old token is valid, so no token listener shold be informed.
const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(24);
task_runner->FastForwardBy(time_to_validation -
base::TimeDelta::FromSeconds(1));
// But when it is time, validation happens.
EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
.WillOnce(
InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
EXPECT_CALL(mock_on_token_callback, Run(_)).Times(0);
task_runner->FastForwardBy(base::TimeDelta::FromSeconds(1));
}
} // namespace syncer } // namespace syncer
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment