// 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/scoped_observation.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/public/data_sharing_service.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 RunGroupsDataSetOrFailureOutcomeCallback(
const JavaRef<jobject>& j_callback,
const DataSharingService::GroupsDataSetOrFailureOutcome& result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_result =
DataSharingConversionBridge::CreateGroupDataSetOrFailureOutcome(env,
result);
RunObjectCallbackAndroid(j_callback, j_result);
}
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) override;
void OnGroupAdded(const GroupData& group_data) override;
void OnGroupRemoved(const GroupId& group_id) 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) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_group =
DataSharingConversionBridge::CreateJavaGroupData(env, group_data);
Java_ObserverBridge_onGroupChanged(env, java_obj_, j_group);
}
void DataSharingServiceAndroid::GroupDataObserverBridge::OnGroupAdded(
const GroupData& group_data) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_group =
DataSharingConversionBridge::CreateJavaGroupData(env, group_data);
Java_ObserverBridge_onGroupAdded(env, java_obj_, j_group);
}
void DataSharingServiceAndroid::GroupDataObserverBridge::OnGroupRemoved(
const GroupId& group_id) {
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))
.obj());
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::ReadAllGroups(
JNIEnv* env,
const JavaParamRef<jobject>& j_callback) {
data_sharing_service_->ReadAllGroups(
base::BindOnce(&RunGroupsDataSetOrFailureOutcomeCallback,
ScopedJavaGlobalRef<jobject>(j_callback)));
}
void DataSharingServiceAndroid::ReadGroup(
JNIEnv* env,
const JavaParamRef<jstring>& group_id,
const JavaParamRef<jobject>& j_callback) {
data_sharing_service_->ReadGroup(
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::DeleteGroup(
JNIEnv* env,
const JavaParamRef<jstring>& group_id,
const JavaParamRef<jobject>& j_callback) {
data_sharing_service_->DeleteGroup(
GroupId(ConvertJavaStringToUTF8(env, group_id)),
base::BindOnce(&RunPeopleGroupActionOutcomeCallback,
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,
const JavaParamRef<jobject>& jcaller) {
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*/ {},
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) {
DataSharingService::ParseURLResult parse_result =
data_sharing_service_->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();
}
ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetJavaObject() {
return ScopedJavaLocalRef<jobject>(java_obj_);
}
ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetJavaObserverBridge() {
JNIEnv* env = base::android::AttachCurrentThread();
return Java_DataSharingServiceImpl_getObserverBridge(env, GetJavaObject());
}
} // namespace data_sharing