blob: 8294c08ed3c78ce740bc8ecb456666d28d9311e9 [file] [log] [blame]
// 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 "chrome/browser/sharing/sharing_device_registration_impl.h"
#include <stdint.h>
#include <vector>
#include "base/base64url.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/gcm_driver/crypto/p256_key_util.h"
#include "components/gcm_driver/instance_id/instance_id_driver.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/prefs/pref_service.h"
#include "components/sharing_message/buildflags.h"
#include "components/sharing_message/pref_names.h"
#include "components/sharing_message/sharing_constants.h"
#include "components/sharing_message/sharing_device_registration_result.h"
#include "components/sharing_message/sharing_sync_preference.h"
#include "components/sharing_message/sharing_target_device_info.h"
#include "components/sharing_message/sharing_utils.h"
#include "components/sync/service/sync_service.h"
#include "components/sync_device_info/device_info.h"
#if BUILDFLAG(IS_ANDROID)
#include "chrome/android/chrome_jni_headers/SharingJNIBridge_jni.h"
#endif
using instance_id::InstanceID;
using sync_pb::SharingSpecificFields;
SharingDeviceRegistrationImpl::SharingDeviceRegistrationImpl(
PrefService* pref_service,
SharingSyncPreference* sharing_sync_preference,
instance_id::InstanceIDDriver* instance_id_driver,
syncer::SyncService* sync_service)
: pref_service_(pref_service),
sharing_sync_preference_(sharing_sync_preference),
instance_id_driver_(instance_id_driver),
sync_service_(sync_service) {}
SharingDeviceRegistrationImpl::~SharingDeviceRegistrationImpl() = default;
void SharingDeviceRegistrationImpl::RegisterDevice(
RegistrationCallback callback) {
if (!CanSendViaSenderID(sync_service_)) {
OnSharingTargetInfoRetrieved(std::move(callback),
SharingDeviceRegistrationResult::kSuccess,
/*sharing_target_info=*/std::nullopt);
return;
}
// Attempt to register using sender ID when enabled.
RetrieveTargetInfo(
kSharingSenderID,
base::BindOnce(
&SharingDeviceRegistrationImpl::OnSharingTargetInfoRetrieved,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void SharingDeviceRegistrationImpl::RetrieveTargetInfo(
const std::string& sender_id,
TargetInfoCallback callback) {
instance_id_driver_->GetInstanceID(kSharingFCMAppID)
->GetToken(
sender_id, instance_id::kGCMScope,
/*time_to_live=*/base::TimeDelta(),
/*flags=*/{InstanceID::Flags::kBypassScheduler},
base::BindOnce(&SharingDeviceRegistrationImpl::OnFCMTokenReceived,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
sender_id));
}
void SharingDeviceRegistrationImpl::OnFCMTokenReceived(
TargetInfoCallback callback,
const std::string& sender_id,
const std::string& fcm_token,
instance_id::InstanceID::Result result) {
switch (result) {
case InstanceID::SUCCESS:
instance_id_driver_->GetInstanceID(kSharingFCMAppID)
->GetEncryptionInfo(
sender_id,
base::BindOnce(
&SharingDeviceRegistrationImpl::OnEncryptionInfoReceived,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
fcm_token));
break;
case InstanceID::NETWORK_ERROR:
case InstanceID::SERVER_ERROR:
case InstanceID::ASYNC_OPERATION_PENDING:
std::move(callback).Run(
SharingDeviceRegistrationResult::kFcmTransientError, std::nullopt);
break;
case InstanceID::INVALID_PARAMETER:
case InstanceID::UNKNOWN_ERROR:
case InstanceID::DISABLED:
std::move(callback).Run(SharingDeviceRegistrationResult::kFcmFatalError,
std::nullopt);
break;
}
}
void SharingDeviceRegistrationImpl::OnEncryptionInfoReceived(
TargetInfoCallback callback,
const std::string& fcm_token,
std::string p256dh,
std::string auth_secret) {
std::move(callback).Run(
SharingDeviceRegistrationResult::kSuccess,
std::make_optional(syncer::DeviceInfo::SharingTargetInfo{
fcm_token, p256dh, auth_secret}));
}
void SharingDeviceRegistrationImpl::OnSharingTargetInfoRetrieved(
RegistrationCallback callback,
SharingDeviceRegistrationResult result,
std::optional<syncer::DeviceInfo::SharingTargetInfo> sharing_target_info) {
if (result != SharingDeviceRegistrationResult::kSuccess) {
std::move(callback).Run(result);
return;
}
if (!sharing_target_info) {
std::move(callback).Run(SharingDeviceRegistrationResult::kInternalError);
return;
}
std::set<SharingSpecificFields::EnabledFeatures> enabled_features =
GetEnabledFeatures();
syncer::DeviceInfo::SharingInfo sharing_info(
sharing_target_info ? std::move(*sharing_target_info)
: syncer::DeviceInfo::SharingTargetInfo(),
/*chime_representative_target_id=*/std::string(),
std::move(enabled_features));
sharing_sync_preference_->SetLocalSharingInfo(std::move(sharing_info));
sharing_sync_preference_->SetFCMRegistration(
SharingSyncPreference::FCMRegistration(base::Time::Now()));
std::move(callback).Run(SharingDeviceRegistrationResult::kSuccess);
}
void SharingDeviceRegistrationImpl::UnregisterDevice(
RegistrationCallback callback) {
auto registration = sharing_sync_preference_->GetFCMRegistration();
if (!registration) {
std::move(callback).Run(
SharingDeviceRegistrationResult::kDeviceNotRegistered);
return;
}
sharing_sync_preference_->ClearLocalSharingInfo();
DeleteFCMToken(kSharingSenderID, std::move(callback));
}
void SharingDeviceRegistrationImpl::DeleteFCMToken(
const std::string& sender_id,
RegistrationCallback callback) {
instance_id_driver_->GetInstanceID(kSharingFCMAppID)
->DeleteToken(
sender_id, instance_id::kGCMScope,
base::BindOnce(&SharingDeviceRegistrationImpl::OnFCMTokenDeleted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void SharingDeviceRegistrationImpl::OnFCMTokenDeleted(
RegistrationCallback callback,
InstanceID::Result result) {
switch (result) {
case InstanceID::SUCCESS:
// INVALID_PARAMETER is expected if InstanceID.GetToken hasn't been
// invoked since restart.
case InstanceID::INVALID_PARAMETER:
sharing_sync_preference_->ClearFCMRegistration();
std::move(callback).Run(SharingDeviceRegistrationResult::kSuccess);
return;
case InstanceID::NETWORK_ERROR:
case InstanceID::SERVER_ERROR:
case InstanceID::ASYNC_OPERATION_PENDING:
std::move(callback).Run(
SharingDeviceRegistrationResult::kFcmTransientError);
return;
case InstanceID::UNKNOWN_ERROR:
case InstanceID::DISABLED:
std::move(callback).Run(SharingDeviceRegistrationResult::kFcmFatalError);
return;
}
NOTREACHED();
}
std::set<SharingSpecificFields::EnabledFeatures>
SharingDeviceRegistrationImpl::GetEnabledFeatures() const {
// Used in tests
if (enabled_features_testing_value_) {
return enabled_features_testing_value_.value();
}
std::set<SharingSpecificFields::EnabledFeatures> enabled_features;
if (IsClickToCallSupported()) {
enabled_features.insert(SharingSpecificFields::CLICK_TO_CALL_V2);
}
if (IsSharedClipboardSupported()) {
enabled_features.insert(SharingSpecificFields::SHARED_CLIPBOARD_V2);
}
if (IsSmsFetcherSupported()) {
enabled_features.insert(SharingSpecificFields::SMS_FETCHER);
}
if (IsRemoteCopySupported()) {
enabled_features.insert(SharingSpecificFields::REMOTE_COPY);
}
if (IsOptimizationGuidePushNotificationSupported()) {
enabled_features.insert(
SharingSpecificFields::OPTIMIZATION_GUIDE_PUSH_NOTIFICATION);
}
#if BUILDFLAG(ENABLE_DISCOVERY)
enabled_features.insert(SharingSpecificFields::DISCOVERY);
#endif
return enabled_features;
}
bool SharingDeviceRegistrationImpl::IsClickToCallSupported() const {
#if BUILDFLAG(IS_ANDROID)
JNIEnv* env = jni_zero::AttachCurrentThread();
return Java_SharingJNIBridge_isTelephonySupported(env);
#else
return false;
#endif
}
bool SharingDeviceRegistrationImpl::IsSharedClipboardSupported() const {
// Check the enterprise policy for Shared Clipboard.
if (pref_service_ &&
!pref_service_->GetBoolean(prefs::kSharedClipboardEnabled)) {
return false;
}
return true;
}
bool SharingDeviceRegistrationImpl::IsSmsFetcherSupported() const {
#if BUILDFLAG(IS_ANDROID)
return true;
#else
return false;
#endif
}
bool SharingDeviceRegistrationImpl::IsRemoteCopySupported() const {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS)
return true;
#else
return false;
#endif
}
bool SharingDeviceRegistrationImpl::
IsOptimizationGuidePushNotificationSupported() const {
return optimization_guide::features::IsOptimizationHintsEnabled() &&
optimization_guide::features::IsPushNotificationsEnabled();
}
void SharingDeviceRegistrationImpl::SetEnabledFeaturesForTesting(
std::set<SharingSpecificFields::EnabledFeatures> enabled_features) {
enabled_features_testing_value_ = std::move(enabled_features);
}