blob: e73ba0e5be8b7e4f6fd2550588691d1c6903f40b [file] [log] [blame]
// Copyright 2019 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 "chrome/browser/sharing/sharing_fcm_handler.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "chrome/browser/sharing/sharing_constants.h"
#include "chrome/browser/sharing/sharing_fcm_sender.h"
#include "chrome/browser/sharing/sharing_message_handler.h"
#include "chrome/browser/sharing/sharing_metrics.h"
#include "chrome/browser/sharing/sharing_service_factory.h"
#include "chrome/browser/sharing/sharing_sync_preference.h"
#include "components/gcm_driver/gcm_driver.h"
#include "third_party/re2/src/re2/re2.h"
namespace {
// The regex captures
// Group 1: type:timesmap
// Group 2: userId#
// Group 3: hashcode
const char kMessageIdRegexPattern[] = "(0:[0-9]+%)([0-9]+#)?([a-f0-9]+)";
// Returns message_id with userId stripped.
// FCM message_id is a persistent id in format of:
// 0:1416811810537717%0#e7a71353318775c7
// ^ ^ ^ ^
// type : timestamp % userId # hashcode
// As per go/persistent-id, userId# is optional, and should be stripped
// comparing persistent ids.
// Retrns |message_id| with userId stripped, or |message_id| if it is not
// confined to the format.
std::string GetStrippedMessageId(const std::string& message_id) {
std::string stripped_message_id, type_timestamp, hashcode;
static const base::NoDestructor<re2::RE2> kMessageIdRegex(
kMessageIdRegexPattern);
if (!re2::RE2::FullMatch(message_id, *kMessageIdRegex, &type_timestamp,
nullptr, &hashcode)) {
return message_id;
}
return base::StrCat({type_timestamp, hashcode});
}
} // namespace
SharingFCMHandler::SharingFCMHandler(gcm::GCMDriver* gcm_driver,
SharingFCMSender* sharing_fcm_sender,
SharingSyncPreference* sync_preference)
: gcm_driver_(gcm_driver),
sharing_fcm_sender_(sharing_fcm_sender),
sync_preference_(sync_preference) {}
SharingFCMHandler::~SharingFCMHandler() {
StopListening();
}
void SharingFCMHandler::StartListening() {
if (!is_listening_) {
gcm_driver_->AddAppHandler(kSharingFCMAppID, this);
is_listening_ = true;
}
}
void SharingFCMHandler::StopListening() {
if (is_listening_) {
gcm_driver_->RemoveAppHandler(kSharingFCMAppID);
is_listening_ = false;
}
}
void SharingFCMHandler::AddSharingHandler(
const SharingMessage::PayloadCase& payload_case,
SharingMessageHandler* handler) {
DCHECK(handler) << "Received request to add null handler";
DCHECK(payload_case != SharingMessage::PAYLOAD_NOT_SET)
<< "Incorrect payload type specified for handler";
DCHECK(!sharing_handlers_.count(payload_case)) << "Handler already exists";
sharing_handlers_[payload_case] = handler;
}
void SharingFCMHandler::RemoveSharingHandler(
const SharingMessage::PayloadCase& payload_case) {
sharing_handlers_.erase(payload_case);
}
void SharingFCMHandler::OnMessagesDeleted(const std::string& app_id) {
// TODO: Handle message deleted from the server.
}
void SharingFCMHandler::ShutdownHandler() {
is_listening_ = false;
}
void SharingFCMHandler::OnMessage(const std::string& app_id,
const gcm::IncomingMessage& message) {
std::string message_id = GetStrippedMessageId(message.message_id);
SharingMessage sharing_message;
if (!sharing_message.ParseFromString(message.raw_data)) {
LOG(ERROR) << "Failed to parse incoming message with id : " << message_id;
return;
}
DCHECK(sharing_message.payload_case() != SharingMessage::PAYLOAD_NOT_SET)
<< "No payload set in SharingMessage received";
LogSharingMessageReceived(sharing_message.payload_case());
auto it = sharing_handlers_.find(sharing_message.payload_case());
if (it == sharing_handlers_.end()) {
LOG(ERROR) << "No handler found for payload : "
<< sharing_message.payload_case();
} else {
it->second->OnMessage(sharing_message);
if (sharing_message.payload_case() != SharingMessage::kAckMessage)
SendAckMessage(sharing_message, message_id);
}
}
void SharingFCMHandler::OnSendAcknowledged(const std::string& app_id,
const std::string& message_id) {
NOTIMPLEMENTED();
}
void SharingFCMHandler::OnSendError(
const std::string& app_id,
const gcm::GCMClient::SendErrorDetails& details) {
NOTIMPLEMENTED();
}
void SharingFCMHandler::OnStoreReset() {
// TODO: Handle GCM store reset.
}
void SharingFCMHandler::SendAckMessage(const SharingMessage& original_message,
const std::string& original_message_id) {
SharingMessage ack_message;
ack_message.mutable_ack_message()->set_original_message_id(
original_message_id);
base::Optional<SharingSyncPreference::Device> target;
if (original_message.has_sender_info()) {
auto& sender_info = original_message.sender_info();
target.emplace(sender_info.fcm_token(), sender_info.p256dh(),
sender_info.auth_secret(),
/*capabilities=*/0);
} else {
target = sync_preference_->GetSyncedDevice(original_message.sender_guid());
if (!target) {
LOG(ERROR) << "Unable to find device in preference";
LogSendSharingAckMessageResult(SharingSendMessageResult::kDeviceNotFound);
return;
}
}
sharing_fcm_sender_->SendMessageToDevice(
std::move(*target), kAckTimeToLive, std::move(ack_message),
base::BindOnce(&SharingFCMHandler::OnAckMessageSent,
weak_ptr_factory_.GetWeakPtr(), original_message_id));
}
void SharingFCMHandler::OnAckMessageSent(
const std::string& original_message_id,
SharingSendMessageResult result,
base::Optional<std::string> message_id) {
LogSendSharingAckMessageResult(result);
if (result != SharingSendMessageResult::kSuccessful)
LOG(ERROR) << "Failed to send ack mesage for " << original_message_id;
}