| // 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 "components/data_sharing/internal/android/data_sharing_service_android.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/android/callback_android.h" |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/notimplemented.h" |
| #include "base/scoped_observation.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "components/data_sharing/internal/android/data_sharing_conversion_bridge.h" |
| #include "components/data_sharing/internal/android/data_sharing_network_loader_android.h" |
| #include "components/data_sharing/internal/android/fake_preview_server_proxy.h" |
| #include "components/data_sharing/internal/data_sharing_service_impl.h" |
| #include "components/data_sharing/internal/preview_server_proxy.h" |
| #include "components/data_sharing/public/android/conversion_utils.h" |
| #include "components/data_sharing/public/data_sharing_service.h" |
| #include "components/data_sharing/public/logger.h" |
| #include "components/data_sharing/public/logger_common.mojom.h" |
| #include "components/data_sharing/public/logger_utils.h" |
| #include "url/android/gurl_android.h" |
| |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "components/data_sharing/internal/jni_headers/DataSharingServiceImpl_jni.h" |
| #include "components/data_sharing/internal/jni_headers/ObserverBridge_jni.h" |
| |
| using base::android::AttachCurrentThread; |
| using base::android::ConvertJavaStringToUTF8; |
| using base::android::ConvertUTF8ToJavaString; |
| using base::android::JavaParamRef; |
| using base::android::RunObjectCallbackAndroid; |
| using base::android::ScopedJavaGlobalRef; |
| using base::android::ScopedJavaLocalRef; |
| |
| namespace data_sharing { |
| namespace { |
| |
| const char kDataSharingServiceBridgeKey[] = "data_sharing_service_bridge"; |
| |
| void RunGroupDataOrFailureOutcomeCallback( |
| const JavaRef<jobject>& j_callback, |
| const DataSharingService::GroupDataOrFailureOutcome& result) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_result = |
| DataSharingConversionBridge::CreateGroupDataOrFailureOutcome(env, result); |
| RunObjectCallbackAndroid(j_callback, j_result); |
| } |
| |
| void RunPeopleGroupActionOutcomeCallback( |
| const JavaRef<jobject>& j_callback, |
| DataSharingService::PeopleGroupActionOutcome result) { |
| ScopedJavaLocalRef<jobject> j_result = |
| DataSharingConversionBridge::CreatePeopleGroupActionOutcome( |
| AttachCurrentThread(), static_cast<int>(result)); |
| RunObjectCallbackAndroid(j_callback, j_result); |
| } |
| |
| void RunSharedDataPreviewOrFailureOutcomeCallback( |
| const JavaRef<jobject>& j_callback, |
| const DataSharingService::SharedDataPreviewOrFailureOutcome& result) { |
| ScopedJavaLocalRef<jobject> j_result = |
| DataSharingConversionBridge::CreateSharedDataPreviewOrFailureOutcome( |
| AttachCurrentThread(), result); |
| RunObjectCallbackAndroid(j_callback, j_result); |
| } |
| |
| } // namespace |
| |
| // Native counterpart of Java ObserverBridge. Observes the native service and |
| // sends notifications to the Java bridge. |
| class DataSharingServiceAndroid::GroupDataObserverBridge |
| : public DataSharingService::Observer { |
| public: |
| GroupDataObserverBridge( |
| DataSharingService* data_sharing_service, |
| DataSharingServiceAndroid* data_sharing_service_android); |
| ~GroupDataObserverBridge() override; |
| |
| GroupDataObserverBridge(const GroupDataObserverBridge&) = delete; |
| GroupDataObserverBridge& operator=(const GroupDataObserverBridge&) = delete; |
| |
| // DataSharingService::Observer impl: |
| void OnGroupChanged(const GroupData& group_data, |
| const base::Time& event_time) override; |
| void OnGroupAdded(const GroupData& group_data, |
| const base::Time& event_time) override; |
| void OnGroupRemoved(const GroupId& group_id, |
| const base::Time& event_time) override; |
| |
| private: |
| ScopedJavaGlobalRef<jobject> java_obj_; |
| base::ScopedObservation<DataSharingService, DataSharingService::Observer> |
| scoped_obs_{this}; |
| }; |
| |
| DataSharingServiceAndroid::GroupDataObserverBridge::GroupDataObserverBridge( |
| DataSharingService* data_sharing_service, |
| DataSharingServiceAndroid* data_sharing_service_android) { |
| java_obj_ = ScopedJavaGlobalRef<jobject>( |
| AttachCurrentThread(), |
| data_sharing_service_android->GetJavaObserverBridge()); |
| scoped_obs_.Observe(data_sharing_service); |
| } |
| |
| DataSharingServiceAndroid::GroupDataObserverBridge::~GroupDataObserverBridge() = |
| default; |
| |
| void DataSharingServiceAndroid::GroupDataObserverBridge::OnGroupChanged( |
| const GroupData& group_data, const base::Time& event_time) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_group = |
| data_sharing::conversion::CreateJavaGroupData(env, group_data); |
| Java_ObserverBridge_onGroupChanged(env, java_obj_, j_group); |
| } |
| |
| void DataSharingServiceAndroid::GroupDataObserverBridge::OnGroupAdded( |
| const GroupData& group_data, const base::Time& event_time) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_group = |
| data_sharing::conversion::CreateJavaGroupData(env, group_data); |
| Java_ObserverBridge_onGroupAdded(env, java_obj_, j_group); |
| } |
| |
| void DataSharingServiceAndroid::GroupDataObserverBridge::OnGroupRemoved( |
| const GroupId& group_id, const base::Time& event_time) { |
| JNIEnv* env = AttachCurrentThread(); |
| Java_ObserverBridge_onGroupRemoved( |
| env, java_obj_, ConvertUTF8ToJavaString(env, group_id.value())); |
| } |
| |
| // This function is declared in data_sharing_service.h and |
| // should be linked in to any binary using |
| // DataSharingService::GetJavaObject. |
| // static |
| ScopedJavaLocalRef<jobject> DataSharingService::GetJavaObject( |
| DataSharingService* service) { |
| if (!service->GetUserData(kDataSharingServiceBridgeKey)) { |
| service->SetUserData(kDataSharingServiceBridgeKey, |
| std::make_unique<DataSharingServiceAndroid>(service)); |
| } |
| |
| DataSharingServiceAndroid* bridge = static_cast<DataSharingServiceAndroid*>( |
| service->GetUserData(kDataSharingServiceBridgeKey)); |
| |
| return bridge->GetJavaObject(); |
| } |
| |
| DataSharingServiceAndroid::DataSharingServiceAndroid( |
| DataSharingService* data_sharing_service) |
| : data_sharing_service_(data_sharing_service), |
| network_loader_(std::make_unique<DataSharingNetworkLoaderAndroid>( |
| data_sharing_service->GetDataSharingNetworkLoader())) { |
| DCHECK(data_sharing_service_); |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| java_obj_.Reset(env, Java_DataSharingServiceImpl_create( |
| env, reinterpret_cast<int64_t>(this))); |
| observer_bridge_ = |
| std::make_unique<GroupDataObserverBridge>(data_sharing_service, this); |
| } |
| |
| DataSharingServiceAndroid::~DataSharingServiceAndroid() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_DataSharingServiceImpl_clearNativePtr(env, java_obj_); |
| } |
| |
| void DataSharingServiceAndroid::ReadGroup( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& group_id, |
| const JavaParamRef<jobject>& j_callback) { |
| // TODO(crbug.com/382033539): migrate android implementation to use |
| // synchronous ReadGroup(). |
| data_sharing_service_->ReadGroupDeprecated( |
| GroupId(ConvertJavaStringToUTF8(env, group_id)), |
| base::BindOnce(&RunGroupDataOrFailureOutcomeCallback, |
| ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| void DataSharingServiceAndroid::CreateGroup( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& group_name, |
| const JavaParamRef<jobject>& j_callback) { |
| data_sharing_service_->CreateGroup( |
| ConvertJavaStringToUTF8(env, group_name), |
| base::BindOnce(&RunGroupDataOrFailureOutcomeCallback, |
| ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| void DataSharingServiceAndroid::InviteMember( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& group_id, |
| const JavaParamRef<jstring>& invitee_email, |
| const JavaParamRef<jobject>& j_callback) { |
| data_sharing_service_->InviteMember( |
| GroupId(ConvertJavaStringToUTF8(env, group_id)), |
| ConvertJavaStringToUTF8(env, invitee_email), |
| base::BindOnce(&RunPeopleGroupActionOutcomeCallback, |
| ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| void DataSharingServiceAndroid::AddMember( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& group_id, |
| const JavaParamRef<jstring>& access_token, |
| const JavaParamRef<jobject>& j_callback) { |
| data_sharing_service_->AddMember( |
| GroupId(ConvertJavaStringToUTF8(env, group_id)), |
| ConvertJavaStringToUTF8(env, access_token), |
| base::BindOnce(&RunPeopleGroupActionOutcomeCallback, |
| ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| void DataSharingServiceAndroid::RemoveMember( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& group_id, |
| const JavaParamRef<jstring>& member_email, |
| const JavaParamRef<jobject>& j_callback) { |
| data_sharing_service_->RemoveMember( |
| GroupId(ConvertJavaStringToUTF8(env, group_id)), |
| ConvertJavaStringToUTF8(env, member_email), |
| base::BindOnce(&RunPeopleGroupActionOutcomeCallback, |
| ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| bool DataSharingServiceAndroid::IsEmptyService(JNIEnv* env) { |
| return data_sharing_service_->IsEmptyService(); |
| } |
| |
| ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetNetworkLoader( |
| JNIEnv* env) { |
| return network_loader_->GetJavaObject(); |
| } |
| |
| ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetDataSharingUrl( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& j_group_id, |
| const JavaParamRef<jstring>& j_access_token) { |
| // Note that this function is only passing the required fields to the native |
| // service. |
| std::string group_id = ConvertJavaStringToUTF8(env, j_group_id); |
| std::string access_token = ConvertJavaStringToUTF8(env, j_access_token); |
| std::unique_ptr<GURL> url = data_sharing_service_->GetDataSharingUrl( |
| GroupData(GroupId(group_id), /*display_name*/ "", /*members*/ {}, |
| /*former_members*/ {}, access_token)); |
| |
| if (url) { |
| return url::GURLAndroid::FromNativeGURL(env, *url); |
| } else { |
| return ScopedJavaLocalRef<jobject>(); |
| } |
| } |
| |
| ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::ParseDataSharingUrl( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& j_url) { |
| ParseUrlResult parse_result = DataSharingUtils::ParseDataSharingUrl( |
| url::GURLAndroid::ToNativeGURL(env, j_url)); |
| return DataSharingConversionBridge::CreateParseUrlResult(env, parse_result); |
| } |
| |
| void DataSharingServiceAndroid::EnsureGroupVisibility( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& group_id, |
| const JavaParamRef<jobject>& j_callback) { |
| data_sharing_service_->EnsureGroupVisibility( |
| GroupId(ConvertJavaStringToUTF8(env, group_id)), |
| base::BindOnce(&RunGroupDataOrFailureOutcomeCallback, |
| ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| void DataSharingServiceAndroid::GetSharedEntitiesPreview( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& group_id, |
| const JavaParamRef<jstring>& access_token, |
| const JavaParamRef<jobject>& j_callback) { |
| data_sharing_service_->GetSharedEntitiesPreview( |
| GroupToken(GroupId(ConvertJavaStringToUTF8(env, group_id)), |
| ConvertJavaStringToUTF8(env, access_token)), |
| base::BindOnce(&RunSharedDataPreviewOrFailureOutcomeCallback, |
| ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetUiDelegate( |
| JNIEnv* env) { |
| return data_sharing_service_->GetUiDelegate()->GetJavaObject(); |
| } |
| |
| void DataSharingServiceAndroid::Log( |
| JNIEnv* env, |
| /*logger_common::mojom::LogSource*/ jint source, |
| const JavaParamRef<jstring>& message) { |
| DATA_SHARING_LOG(static_cast<logger_common::mojom::LogSource>(source), |
| data_sharing_service_->GetLogger(), |
| ConvertJavaStringToUTF8(env, message)); |
| } |
| |
| ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetJavaObject() { |
| return ScopedJavaLocalRef<jobject>(java_obj_); |
| } |
| |
| ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetJavaObserverBridge() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| return Java_DataSharingServiceImpl_getObserverBridge(env, GetJavaObject()); |
| } |
| |
| ScopedJavaLocalRef<jobject> |
| JNI_DataSharingServiceImpl_GetDataSharingUrlForTesting( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& j_group_id, |
| const JavaParamRef<jstring>& j_access_token) { |
| GURL url = *DataSharingServiceImpl::GetDataSharingUrl( |
| GroupToken(GroupId(ConvertJavaStringToUTF8(env, j_group_id)), |
| ConvertJavaStringToUTF8(env, j_access_token))); |
| return url::GURLAndroid::FromNativeGURL(env, url); |
| } |
| |
| void DataSharingServiceAndroid::SetSharedEntitiesPreviewForTesting( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& j_group_id) { |
| auto fake_proxy = std::make_unique<FakePreviewServerProxy>(); |
| data_sharing::SharedTabGroupPreview preview; |
| preview.title = "my group title"; |
| |
| for (int i = 0; i < 3; i++) { |
| preview.tabs.emplace_back( |
| GURL("https://google.com/" + base::NumberToString(i))); |
| } |
| data_sharing::SharedDataPreview sharedDataPreview; |
| sharedDataPreview.shared_tab_group_preview = std::move(preview); |
| data_sharing::DataSharingService::SharedDataPreviewOrFailureOutcome outcome = |
| std::move(sharedDataPreview); |
| |
| fake_proxy->SetSharedEntitiesPreviewForTesting( |
| GroupId(ConvertJavaStringToUTF8(j_group_id)), std::move(outcome)); |
| |
| data_sharing_service_->SetPreviewServerProxyForTesting(std::move(fake_proxy)); |
| } |
| |
| } // namespace data_sharing |