forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
boca: Register invalidation workflow for boca.
Test: Unit tested. E2E test manually. Bug: b:354769102 Low-Coverage-Reason: OTHER session_client_impl.cc is just proxy and has no business logic. Change-Id: I7c0ffc322550305bec97b04ce668c8a21cbeba7b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5854834 Reviewed-by: Benjamin Zielinski <[email protected]> Commit-Queue: April Zhou <[email protected]> Cr-Commit-Position: refs/heads/main@{#1356170}
- Loading branch information
Showing
9 changed files
with
335 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -87,7 +87,6 @@ class MockBocaAppClient : public BocaAppClient { | |
|
||
constexpr char kTestGaiaId[] = "123"; | ||
constexpr char kTestUserEmail[] = "[email protected]"; | ||
} // namespace | ||
|
||
class BocaSessionManagerTest : public testing::Test { | ||
public: | ||
|
@@ -649,4 +648,5 @@ TEST_F(BocaSessionManagerTest, NotifyLocalCaptionConfigWhenLocalChange) { | |
BocaAppClient::Get()->GetSessionManager()->NotifyLocalCaptionEvents(config); | ||
} | ||
|
||
} // namespace | ||
} // namespace ash::boca |
76 changes: 76 additions & 0 deletions
76
chromeos/ash/components/boca/invalidations/invalidation_service_impl.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// Copyright 2024 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "chromeos/ash/components/boca/invalidations/invalidation_service_impl.h" | ||
|
||
#include "base/time/time.h" | ||
#include "chromeos/ash/components/boca/boca_session_manager.h" | ||
#include "chromeos/ash/components/boca/session_api/session_client_impl.h" | ||
#include "chromeos/ash/components/boca/session_api/upload_token_request.h" | ||
#include "components/gcm_driver/gcm_driver.h" | ||
#include "components/gcm_driver/instance_id/instance_id_driver.h" | ||
|
||
namespace ash::boca { | ||
|
||
InvalidationServiceImpl::InvalidationServiceImpl( | ||
gcm::GCMDriver* gcm_driver, | ||
instance_id::InstanceIDDriver* instance_id_driver, | ||
AccountId account_id, | ||
BocaSessionManager* boca_session_manager, | ||
SessionClientImpl* session_client_impl) | ||
: account_id_(account_id), | ||
boca_session_manager_(boca_session_manager), | ||
session_client_impl_(session_client_impl) { | ||
fcm_handler_ = std::make_unique<FCMHandler>(gcm_driver, instance_id_driver, | ||
kSenderId, kApplicationId); | ||
// Add token refresh observer. | ||
fcm_handler_->AddTokenObserver(this); | ||
// Add invalidation message observer. | ||
fcm_handler_->AddListener(this); | ||
// Register app handler and start token fetch. | ||
fcm_handler_->StartListening(); | ||
} | ||
|
||
InvalidationServiceImpl::~InvalidationServiceImpl() { | ||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | ||
fcm_handler_->RemoveTokenObserver(this); | ||
fcm_handler_->RemoveListener(this); | ||
} | ||
|
||
void InvalidationServiceImpl::OnInvalidationReceived( | ||
const std::string& payload) { | ||
// TODO(b/354769102): Potentially validate FCM payload before dispatching. And | ||
// implement a thread-safe approach to skip loading when there is already | ||
// active loading in progress. | ||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | ||
// FCM message will be delivered even user not logged in. But | ||
// LoadCurrentSession has a validation to skip load if the current active user | ||
// doesn't match the profile user. | ||
boca_session_manager_->LoadCurrentSession(); | ||
} | ||
|
||
void InvalidationServiceImpl::OnFCMRegistrationTokenChanged() { | ||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | ||
if (!fcm_handler_->GetFCMRegistrationToken().has_value()) { | ||
return; | ||
} else { | ||
auto request = std::make_unique<UploadTokenRequest>( | ||
session_client_impl_->sender(), account_id_.GetGaiaId(), | ||
fcm_handler_->GetFCMRegistrationToken().value(), | ||
base::BindOnce(&InvalidationServiceImpl::OnTokenUploaded, | ||
weak_factory_.GetWeakPtr())); | ||
session_client_impl_->UploadToken(std::move(request)); | ||
} | ||
} | ||
|
||
void InvalidationServiceImpl::OnTokenUploaded( | ||
base::expected<bool, google_apis::ApiErrorCode> result) { | ||
if (result.has_value()) { | ||
// TODO(b/366316261):Add metrics for token failure. | ||
LOG(WARNING) << "[Boca]Failed to upload token, skipping"; | ||
return; | ||
} | ||
} | ||
|
||
} // namespace ash::boca |
60 changes: 60 additions & 0 deletions
60
chromeos/ash/components/boca/invalidations/invalidation_service_impl.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright 2024 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef CHROMEOS_ASH_COMPONENTS_BOCA_INVALIDATIONS_INVALIDATION_SERVICE_IMPL_H_ | ||
#define CHROMEOS_ASH_COMPONENTS_BOCA_INVALIDATIONS_INVALIDATION_SERVICE_IMPL_H_ | ||
|
||
#include <memory> | ||
|
||
#include "base/time/time.h" | ||
#include "base/types/expected.h" | ||
#include "chromeos/ash/components/boca/invalidations/fcm_handler.h" | ||
#include "components/account_id/account_id.h" | ||
#include "google_apis/common/api_error_codes.h" | ||
|
||
namespace instance_id { | ||
class InstanceIDDriver; | ||
} | ||
|
||
namespace gcm { | ||
class GCMDriver; | ||
} | ||
|
||
namespace ash::boca { | ||
class BocaSessionManager; | ||
class SessionClientImpl; | ||
class InvalidationServiceImpl : public InvalidationsListener, | ||
FCMRegistrationTokenObserver { | ||
public: | ||
inline static constexpr char kSenderId[] = "947897361853"; | ||
inline static constexpr char kApplicationId[] = | ||
"com.google.chrome.boca.fcm.invalidations"; | ||
InvalidationServiceImpl(gcm::GCMDriver* gcm_driver, | ||
instance_id::InstanceIDDriver* instance_id_driver, | ||
AccountId account_id, | ||
BocaSessionManager* boca_session_manager_, | ||
SessionClientImpl* session_client_impl_); | ||
~InvalidationServiceImpl() override; | ||
|
||
// InvalidationsListener implementation. | ||
void OnInvalidationReceived(const std::string& payload) override; | ||
|
||
// FCMRegistrationTokenObserver implementation. | ||
void OnFCMRegistrationTokenChanged() override; | ||
|
||
void OnTokenUploaded(base::expected<bool, google_apis::ApiErrorCode> result); | ||
|
||
FCMHandler* fcm_handler() { return fcm_handler_.get(); } | ||
|
||
private: | ||
SEQUENCE_CHECKER(sequence_checker_); | ||
std::unique_ptr<FCMHandler> fcm_handler_; | ||
AccountId account_id_; | ||
raw_ptr<BocaSessionManager> boca_session_manager_; | ||
raw_ptr<SessionClientImpl> session_client_impl_; | ||
base::WeakPtrFactory<InvalidationServiceImpl> weak_factory_{this}; | ||
}; | ||
|
||
} // namespace ash::boca | ||
#endif // CHROMEOS_ASH_COMPONENTS_BOCA_INVALIDATIONS_INVALIDATION_SERVICE_IMPL_H_ |
184 changes: 184 additions & 0 deletions
184
chromeos/ash/components/boca/invalidations/invalidation_service_impl_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// Copyright 2024 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "chromeos/ash/components/boca/invalidations/invalidation_service_impl.h" | ||
|
||
#include <memory> | ||
|
||
#include "base/test/gmock_callback_support.h" | ||
#include "base/test/task_environment.h" | ||
#include "chromeos/ash/components/boca/boca_session_manager.h" | ||
#include "chromeos/ash/components/boca/invalidations/fcm_handler.h" | ||
#include "chromeos/ash/components/boca/session_api/session_client_impl.h" | ||
#include "chromeos/ash/components/boca/session_api/upload_token_request.h" | ||
#include "components/account_id/account_id.h" | ||
#include "components/gcm_driver/fake_gcm_driver.h" | ||
#include "components/gcm_driver/gcm_driver.h" | ||
#include "components/gcm_driver/instance_id/instance_id_driver.h" | ||
#include "google_apis/common/request_sender.h" | ||
#include "testing/gmock/include/gmock/gmock.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
using ::base::test::RunOnceCallback; | ||
using ::testing::_; | ||
using ::testing::NiceMock; | ||
using ::testing::Return; | ||
using ::testing::SaveArg; | ||
|
||
namespace ash::boca { | ||
|
||
namespace { | ||
constexpr char kTestEmail[] = "testemail"; | ||
constexpr char kGaiaId[] = "123"; | ||
constexpr int kTokenValidationPeriodMinutesDefault = 60 * 24; | ||
|
||
class MockSessionClientImpl : public SessionClientImpl { | ||
public: | ||
explicit MockSessionClientImpl( | ||
std::unique_ptr<google_apis::RequestSender> sender) | ||
: SessionClientImpl(std::move(sender)) {} | ||
MOCK_METHOD(void, | ||
UploadToken, | ||
(std::unique_ptr<UploadTokenRequest>), | ||
(override)); | ||
}; | ||
|
||
class MockSessionManager : public BocaSessionManager { | ||
public: | ||
explicit MockSessionManager(SessionClientImpl* session_client_impl) | ||
: BocaSessionManager( | ||
session_client_impl, | ||
AccountId::FromUserEmailGaiaId(kTestEmail, kGaiaId)) {} | ||
MOCK_METHOD(void, LoadCurrentSession, (), (override)); | ||
~MockSessionManager() override = default; | ||
}; | ||
|
||
class MockGCMDriver : public gcm::GCMDriver { | ||
public: | ||
MockGCMDriver(base::FilePath& path, | ||
scoped_refptr<base::SequencedTaskRunner>& sequence_runner) | ||
: GCMDriver(/*store_path=*/base::FilePath(), | ||
/*blocking_task_runner=*/nullptr) {} | ||
}; | ||
|
||
class MockInstanceID : public instance_id::InstanceID { | ||
public: | ||
MockInstanceID() | ||
: instance_id::InstanceID(InvalidationServiceImpl::kApplicationId, | ||
/*gcm_driver=*/nullptr) {} | ||
~MockInstanceID() override = default; | ||
MOCK_METHOD(void, GetID, (GetIDCallback callback), (override)); | ||
MOCK_METHOD(void, | ||
GetCreationTime, | ||
(GetCreationTimeCallback callback), | ||
(override)); | ||
MOCK_METHOD(void, | ||
GetToken, | ||
(const std::string& authorized_entity, | ||
const std::string& scope, | ||
base::TimeDelta time_to_live, | ||
std::set<Flags> flags, | ||
GetTokenCallback callback), | ||
(override)); | ||
MOCK_METHOD(void, | ||
ValidateToken, | ||
(const std::string& authorized_entity, | ||
const std::string& scope, | ||
const std::string& token, | ||
ValidateTokenCallback callback), | ||
(override)); | ||
|
||
protected: | ||
MOCK_METHOD(void, | ||
DeleteTokenImpl, | ||
(const std::string& authorized_entity, | ||
const std::string& scope, | ||
DeleteTokenCallback callback), | ||
(override)); | ||
MOCK_METHOD(void, DeleteIDImpl, (DeleteIDCallback callback), (override)); | ||
}; | ||
|
||
class MockInstanceIDDriver : public instance_id::InstanceIDDriver { | ||
public: | ||
MockInstanceIDDriver() : InstanceIDDriver(/*gcm_driver=*/nullptr) {} | ||
~MockInstanceIDDriver() override = default; | ||
MOCK_METHOD(instance_id::InstanceID*, | ||
GetInstanceID, | ||
(const std::string& app_id), | ||
(override)); | ||
MOCK_METHOD(void, RemoveInstanceID, (const std::string& app_id), (override)); | ||
MOCK_METHOD(bool, | ||
ExistsInstanceID, | ||
(const std::string& app_id), | ||
(const override)); | ||
}; | ||
|
||
class InvalidationServiceImplTest : public testing::Test { | ||
protected: | ||
InvalidationServiceImplTest() = default; | ||
~InvalidationServiceImplTest() override = default; | ||
|
||
void SetUp() override { | ||
mock_instance_id_driver_ = | ||
std::make_unique<NiceMock<MockInstanceIDDriver>>(); | ||
ON_CALL(*mock_instance_id_driver_, | ||
GetInstanceID(InvalidationServiceImpl::kApplicationId)) | ||
.WillByDefault(Return(&mock_instance_id_)); | ||
|
||
ON_CALL(mock_instance_id_, GetToken) | ||
.WillByDefault(RunOnceCallback<4>( | ||
"default_token", instance_id::InstanceID::Result::SUCCESS)); | ||
|
||
session_client_impl_ = | ||
std::make_unique<NiceMock<MockSessionClientImpl>>(nullptr); | ||
ON_CALL(*session_client_impl_, UploadToken(_)).WillByDefault(Return()); | ||
|
||
boca_session_manager_ = std::make_unique<NiceMock<MockSessionManager>>( | ||
session_client_impl_.get()); | ||
invalidation_service_impl_ = std::make_unique<InvalidationServiceImpl>( | ||
&fake_gcm_driver_, mock_instance_id_driver_.get(), | ||
AccountId::FromUserEmailGaiaId(kTestEmail, kGaiaId), | ||
boca_session_manager_.get(), session_client_impl_.get()); | ||
} | ||
|
||
base::test::SingleThreadTaskEnvironment task_environment_{ | ||
base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME}; | ||
NiceMock<MockInstanceID> mock_instance_id_; | ||
gcm::FakeGCMDriver fake_gcm_driver_; | ||
std::unique_ptr<NiceMock<MockInstanceIDDriver>> mock_instance_id_driver_; | ||
std::unique_ptr<NiceMock<MockSessionClientImpl>> session_client_impl_; | ||
std::unique_ptr<NiceMock<MockSessionManager>> boca_session_manager_; | ||
std::unique_ptr<InvalidationServiceImpl> invalidation_service_impl_; | ||
}; | ||
|
||
TEST_F(InvalidationServiceImplTest, HandleInvalidation) { | ||
EXPECT_CALL(*boca_session_manager_, LoadCurrentSession()).Times(1); | ||
const std::string kPayloadValue = "payload_1"; | ||
gcm::IncomingMessage gcm_message; | ||
gcm_message.raw_data = kPayloadValue; | ||
invalidation_service_impl_->fcm_handler()->OnMessage( | ||
InvalidationServiceImpl::kApplicationId, gcm_message); | ||
} | ||
|
||
TEST_F(InvalidationServiceImplTest, HandleTokenUpload) { | ||
// Check that the handler gets the token through GetToken. | ||
const char token[] = "token_2"; | ||
EXPECT_CALL(mock_instance_id_, GetToken) | ||
.WillOnce( | ||
RunOnceCallback<4>(token, instance_id::InstanceID::Result::SUCCESS)); | ||
std::unique_ptr<UploadTokenRequest> request; | ||
EXPECT_CALL(*session_client_impl_, UploadToken(_)) | ||
.WillOnce([&](std::unique_ptr<UploadTokenRequest> request_1) { | ||
request = std::move(request_1); | ||
}); | ||
// Adjust the time and check that validation will happen in time. | ||
// The old token is invalid, so token observer should be informed. | ||
task_environment_.FastForwardBy( | ||
base::Minutes(kTokenValidationPeriodMinutesDefault)); | ||
|
||
EXPECT_EQ(kGaiaId, request->gaia_id()); | ||
EXPECT_EQ(token, request->token()); | ||
} | ||
} // namespace | ||
} // namespace ash::boca |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters