blob: df0c5685265ab3ec612f4a01af70643a4958d771 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sharing/sharing_fcm_sender.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/guid.h"
#include "base/trace_event/trace_event.h"
#include "base/version.h"
#include "chrome/browser/sharing/features.h"
#include "chrome/browser/sharing/sharing_constants.h"
#include "chrome/browser/sharing/sharing_message_bridge.h"
#include "chrome/browser/sharing/sharing_sync_preference.h"
#include "chrome/browser/sharing/sharing_utils.h"
#include "chrome/browser/sharing/vapid_key_manager.h"
#include "chrome/browser/sharing/web_push/web_push_sender.h"
#include "components/gcm_driver/crypto/gcm_encryption_result.h"
#include "components/gcm_driver/gcm_driver.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync_device_info/local_device_info_provider.h"
SharingFCMSender::SharingFCMSender(
std::unique_ptr<WebPushSender> web_push_sender,
SharingMessageBridge* sharing_message_bridge,
SharingSyncPreference* sync_preference,
VapidKeyManager* vapid_key_manager,
gcm::GCMDriver* gcm_driver,
syncer::LocalDeviceInfoProvider* local_device_info_provider,
syncer::SyncService* sync_service)
: web_push_sender_(std::move(web_push_sender)),
sharing_message_bridge_(sharing_message_bridge),
sync_preference_(sync_preference),
vapid_key_manager_(vapid_key_manager),
gcm_driver_(gcm_driver),
local_device_info_provider_(local_device_info_provider),
sync_service_(sync_service) {}
SharingFCMSender::~SharingFCMSender() = default;
void SharingFCMSender::DoSendMessageToDevice(const syncer::DeviceInfo& device,
base::TimeDelta time_to_live,
SharingMessage message,
SendMessageCallback callback) {
TRACE_EVENT0("sharing", "SharingFCMSender::DoSendMessageToDevice");
auto fcm_configuration = GetFCMChannel(device);
if (!fcm_configuration) {
std::move(callback).Run(SharingSendMessageResult::kDeviceNotFound,
/*message_id=*/absl::nullopt,
SharingChannelType::kUnknown);
return;
}
if (!SetMessageSenderInfo(&message)) {
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/absl::nullopt,
SharingChannelType::kUnknown);
return;
}
SendMessageToFcmTarget(*fcm_configuration, time_to_live, std::move(message),
std::move(callback));
}
void SharingFCMSender::SendMessageToFcmTarget(
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration,
base::TimeDelta time_to_live,
SharingMessage message,
SendMessageCallback callback) {
TRACE_EVENT0("sharing", "SharingFCMSender::SendMessageToFcmTarget");
bool canSendViaSync =
base::FeatureList::IsEnabled(kSharingSendViaSync) &&
sync_service_->GetActiveDataTypes().Has(syncer::SHARING_MESSAGE) &&
!fcm_configuration.sender_id_fcm_token().empty() &&
!fcm_configuration.sender_id_p256dh().empty() &&
!fcm_configuration.sender_id_auth_secret().empty();
bool canSendViaVapid = !fcm_configuration.vapid_fcm_token().empty() &&
!fcm_configuration.vapid_p256dh().empty() &&
!fcm_configuration.vapid_auth_secret().empty();
if (canSendViaSync && (!canSendViaVapid ||
!base::FeatureList::IsEnabled(kSharingPreferVapid))) {
message.set_message_id(base::GenerateGUID());
EncryptMessage(
kSharingSenderID, fcm_configuration.sender_id_p256dh(),
fcm_configuration.sender_id_auth_secret(), message,
SharingChannelType::kFcmSenderId, std::move(callback),
base::BindOnce(&SharingFCMSender::DoSendMessageToSenderIdTarget,
weak_ptr_factory_.GetWeakPtr(),
fcm_configuration.sender_id_fcm_token(), time_to_live,
message.message_id()));
return;
}
if (canSendViaVapid) {
absl::optional<SharingSyncPreference::FCMRegistration> fcm_registration =
sync_preference_->GetFCMRegistration();
if (!fcm_registration || !fcm_registration->authorized_entity) {
LOG(ERROR) << "Unable to retrieve FCM registration";
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/absl::nullopt,
SharingChannelType::kUnknown);
return;
}
EncryptMessage(
*fcm_registration->authorized_entity, fcm_configuration.vapid_p256dh(),
fcm_configuration.vapid_auth_secret(), message,
SharingChannelType::kFcmVapid, std::move(callback),
base::BindOnce(&SharingFCMSender::DoSendMessageToVapidTarget,
weak_ptr_factory_.GetWeakPtr(),
fcm_configuration.vapid_fcm_token(), time_to_live));
return;
}
std::move(callback).Run(SharingSendMessageResult::kDeviceNotFound,
/*message_id=*/absl::nullopt,
SharingChannelType::kUnknown);
}
void SharingFCMSender::SendMessageToServerTarget(
const chrome_browser_sharing::ServerChannelConfiguration& server_channel,
SharingMessage message,
SendMessageCallback callback) {
TRACE_EVENT0("sharing", "SharingFCMSender::SendMessageToServerTarget");
if (!base::FeatureList::IsEnabled(kSharingSendViaSync) ||
!sync_service_->GetActiveDataTypes().Has(syncer::SHARING_MESSAGE)) {
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/absl::nullopt,
SharingChannelType::kServer);
return;
}
message.set_message_id(base::GenerateGUID());
EncryptMessage(
kSharingSenderID, server_channel.p256dh(), server_channel.auth_secret(),
message, SharingChannelType::kServer, std::move(callback),
base::BindOnce(&SharingFCMSender::DoSendMessageToServerTarget,
weak_ptr_factory_.GetWeakPtr(),
server_channel.configuration(), message.message_id()));
}
void SharingFCMSender::EncryptMessage(const std::string& authorized_entity,
const std::string& p256dh,
const std::string& auth_secret,
const SharingMessage& message,
SharingChannelType channel_type,
SendMessageCallback callback,
MessageSender message_sender) {
std::string payload;
message.SerializeToString(&payload);
gcm_driver_->EncryptMessage(
kSharingFCMAppID, authorized_entity, p256dh, auth_secret, payload,
base::BindOnce(&SharingFCMSender::OnMessageEncrypted,
weak_ptr_factory_.GetWeakPtr(), channel_type,
std::move(callback), std::move(message_sender)));
}
void SharingFCMSender::OnMessageEncrypted(SharingChannelType channel_type,
SendMessageCallback callback,
MessageSender message_sender,
gcm::GCMEncryptionResult result,
std::string message) {
if (result != gcm::GCMEncryptionResult::ENCRYPTED_DRAFT_08) {
LOG(ERROR) << "Unable to encrypt message";
std::move(callback).Run(SharingSendMessageResult::kEncryptionError,
/*message_id=*/absl::nullopt, channel_type);
return;
}
std::move(message_sender).Run(std::move(message), std::move(callback));
}
void SharingFCMSender::DoSendMessageToVapidTarget(
const std::string& fcm_token,
base::TimeDelta time_to_live,
std::string message,
SendMessageCallback callback) {
TRACE_EVENT0("sharing", "SharingFCMSender::DoSendMessageToVapidTarget");
auto* vapid_key = vapid_key_manager_->GetOrCreateKey();
if (!vapid_key) {
LOG(ERROR) << "Unable to retrieve VAPID key";
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/absl::nullopt,
SharingChannelType::kFcmVapid);
return;
}
WebPushMessage web_push_message;
web_push_message.time_to_live = time_to_live.InSeconds();
web_push_message.urgency = WebPushMessage::Urgency::kHigh;
web_push_message.payload = std::move(message);
web_push_sender_->SendMessage(
fcm_token, vapid_key, std::move(web_push_message),
base::BindOnce(&SharingFCMSender::OnMessageSentToVapidTarget,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void SharingFCMSender::OnMessageSentToVapidTarget(
SendMessageCallback callback,
SendWebPushMessageResult result,
absl::optional<std::string> message_id) {
TRACE_EVENT1("sharing", "SharingFCMSender::OnMessageSentToVapidTarget",
"result", result);
SharingSendMessageResult send_message_result;
switch (result) {
case SendWebPushMessageResult::kSuccessful:
send_message_result = SharingSendMessageResult::kSuccessful;
break;
case SendWebPushMessageResult::kDeviceGone:
send_message_result = SharingSendMessageResult::kDeviceNotFound;
break;
case SendWebPushMessageResult::kNetworkError:
send_message_result = SharingSendMessageResult::kNetworkError;
break;
case SendWebPushMessageResult::kPayloadTooLarge:
send_message_result = SharingSendMessageResult::kPayloadTooLarge;
break;
case SendWebPushMessageResult::kEncryptionFailed:
case SendWebPushMessageResult::kCreateJWTFailed:
case SendWebPushMessageResult::kServerError:
case SendWebPushMessageResult::kParseResponseFailed:
case SendWebPushMessageResult::kVapidKeyInvalid:
send_message_result = SharingSendMessageResult::kInternalError;
break;
}
std::move(callback).Run(send_message_result, message_id,
SharingChannelType::kFcmVapid);
}
void SharingFCMSender::DoSendMessageToSenderIdTarget(
const std::string& fcm_token,
base::TimeDelta time_to_live,
const std::string& message_id,
std::string message,
SendMessageCallback callback) {
TRACE_EVENT0("sharing", "SharingFCMSender::DoSendMessageToSenderIdTarget");
// Double check that SHARING_MESSAGE is syncing.
if (!sync_service_->GetActiveDataTypes().Has(syncer::SHARING_MESSAGE)) {
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/absl::nullopt,
SharingChannelType::kFcmSenderId);
return;
}
auto specifics = std::make_unique<sync_pb::SharingMessageSpecifics>();
auto* fcm_configuration =
specifics->mutable_channel_configuration()->mutable_fcm();
fcm_configuration->set_token(fcm_token);
fcm_configuration->set_ttl(time_to_live.InSeconds());
fcm_configuration->set_priority(10);
specifics->set_payload(message);
sharing_message_bridge_->SendSharingMessage(
std::move(specifics),
base::BindOnce(&SharingFCMSender::OnMessageSentViaSync,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
message_id, SharingChannelType::kFcmSenderId));
}
void SharingFCMSender::DoSendMessageToServerTarget(
const std::string& server_channel,
const std::string& message_id,
std::string message,
SendMessageCallback callback) {
TRACE_EVENT0("sharing", "SharingFCMSender::DoSendMessageToServerTarget");
// Double check that SHARING_MESSAGE is syncing.
if (!sync_service_->GetActiveDataTypes().Has(syncer::SHARING_MESSAGE)) {
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/absl::nullopt,
SharingChannelType::kServer);
return;
}
auto specifics = std::make_unique<sync_pb::SharingMessageSpecifics>();
specifics->mutable_channel_configuration()->set_server(server_channel);
specifics->set_payload(message);
sharing_message_bridge_->SendSharingMessage(
std::move(specifics),
base::BindOnce(&SharingFCMSender::OnMessageSentViaSync,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
message_id, SharingChannelType::kServer));
}
void SharingFCMSender::OnMessageSentViaSync(
SendMessageCallback callback,
const std::string& message_id,
SharingChannelType channel_type,
const sync_pb::SharingMessageCommitError& error) {
TRACE_EVENT1("sharing", "SharingFCMSender::OnMessageSentViaSync", "error",
error.error_code());
SharingSendMessageResult send_message_result;
switch (error.error_code()) {
case sync_pb::SharingMessageCommitError::NONE:
send_message_result = SharingSendMessageResult::kSuccessful;
break;
case sync_pb::SharingMessageCommitError::NOT_FOUND:
send_message_result = SharingSendMessageResult::kDeviceNotFound;
break;
case sync_pb::SharingMessageCommitError::INVALID_ARGUMENT:
send_message_result = SharingSendMessageResult::kPayloadTooLarge;
break;
case sync_pb::SharingMessageCommitError::INTERNAL:
case sync_pb::SharingMessageCommitError::UNAVAILABLE:
case sync_pb::SharingMessageCommitError::RESOURCE_EXHAUSTED:
case sync_pb::SharingMessageCommitError::UNAUTHENTICATED:
case sync_pb::SharingMessageCommitError::PERMISSION_DENIED:
case sync_pb::SharingMessageCommitError::SYNC_TURNED_OFF:
case sync_pb::SharingMessageCommitError::
DEPRECATED_SYNC_SERVER_OR_AUTH_ERROR:
case sync_pb::SharingMessageCommitError::SYNC_SERVER_ERROR:
case sync_pb::SharingMessageCommitError::SYNC_AUTH_ERROR:
send_message_result = SharingSendMessageResult::kInternalError;
break;
case sync_pb::SharingMessageCommitError::SYNC_NETWORK_ERROR:
send_message_result = SharingSendMessageResult::kNetworkError;
break;
case sync_pb::SharingMessageCommitError::SYNC_TIMEOUT:
send_message_result = SharingSendMessageResult::kCommitTimeout;
break;
}
std::move(callback).Run(send_message_result, message_id, channel_type);
}
bool SharingFCMSender::SetMessageSenderInfo(SharingMessage* message) {
absl::optional<syncer::DeviceInfo::SharingInfo> sharing_info =
local_device_info_provider_->GetLocalDeviceInfo()->sharing_info();
if (!sharing_info)
return false;
auto* fcm_configuration = message->mutable_fcm_channel_configuration();
fcm_configuration->set_vapid_fcm_token(
sharing_info->vapid_target_info.fcm_token);
fcm_configuration->set_vapid_p256dh(sharing_info->vapid_target_info.p256dh);
fcm_configuration->set_vapid_auth_secret(
sharing_info->vapid_target_info.auth_secret);
fcm_configuration->set_sender_id_fcm_token(
sharing_info->sender_id_target_info.fcm_token);
fcm_configuration->set_sender_id_p256dh(
sharing_info->sender_id_target_info.p256dh);
fcm_configuration->set_sender_id_auth_secret(
sharing_info->sender_id_target_info.auth_secret);
return true;
}
void SharingFCMSender::SetWebPushSenderForTesting(
std::unique_ptr<WebPushSender> web_push_sender) {
web_push_sender_ = std::move(web_push_sender);
}
void SharingFCMSender::SetSharingMessageBridgeForTesting(
SharingMessageBridge* sharing_message_bridge) {
sharing_message_bridge_ = sharing_message_bridge;
}