* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "plugins/web/webview/android/java/jni/geolocation_permissions_jni.h"
#include <jni.h>
#include "inner_api/plugin_utils_inner.h"
#include "log.h"
#include "plugin_utils.h"
#include "business_error.h"
using namespace OHOS::NWebError;
namespace OHOS::Plugin {
namespace {
const char WEB_WEBVIEW_CLASS_NAME[] = "ohos/ace/adapter/capability/web/AceGeolocationPermissions";
static const JNINativeMethod METHODS[] = {
{ "nativeInit", "()V", reinterpret_cast<void*>(GeolocationPermissionsJni::NativeInit) },
{ "onReceiveGetAccessibleGeolocationValue", "(ZJ)V",
reinterpret_cast<void*>(GeolocationPermissionsJni::OnReceiveGetAccessibleGeolocationValue) },
{ "onReceiveGetAccessibleGeolocationError", "(Ljava/lang/String;J)V",
reinterpret_cast<void*>(GeolocationPermissionsJni::OnReceiveGetAccessibleGeolocationError) },
{ "onReceiveGetStoredGeolocationValue", "([Ljava/lang/String;J)V",
reinterpret_cast<void*>(GeolocationPermissionsJni::OnReceiveGetStoredGeolocationValue) },
};
static const char METHOD_ALLOW_GEOLOCATION[] = "allowGeolocation";
static const char METHOD_DELETE_GEOLOCATION[] = "deleteGeolocation";
static const char METHOD_DELETE_ALL_GEOLOCATION[] = "deleteAllGeolocation";
static const char METHOD_GET_ACCESSIBLE_GEOLOCATION[] = "getAccessibleGeolocation";
static const char METHOD_GET_STORED_GEOLOCATION[] = "getStoredGeolocation";
static const char SIGNATURE_ALLOW_GEOLOCATION[] = "(Ljava/lang/String;ZZ)V";
static const char SIGNATURE_DELETE_GEOLOCATION[] = "(Ljava/lang/String;Z)V";
static const char SIGNATURE_DELETE_ALL_GEOLOCATION[] = "(Z)V";
static const char SIGNATURE_GET_ACCESSIBLE_GEOLOCATION[] = "(Ljava/lang/String;JZ)V";
static const char SIGNATURE_GET_STORED_GEOLOCATION[] = "(JZ)V";
struct {
jmethodID allowGeolocation;
jmethodID deleteGeolocation;
jmethodID deleteAllGeolocation;
jmethodID getAccessibleGeolocation;
jmethodID getStoredGeolocation;
jobject globalRef;
} g_webWebviewClass;
}
bool GeolocationPermissionsJni::Register(void* env)
{
auto* jniEnv = static_cast<JNIEnv*>(env);
CHECK_NULL_RETURN(jniEnv, false);
jclass cls = jniEnv->FindClass(WEB_WEBVIEW_CLASS_NAME);
CHECK_NULL_RETURN(cls, false);
bool ret = jniEnv->RegisterNatives(cls, METHODS, sizeof(METHODS) / sizeof(METHODS[0])) == 0;
jniEnv->DeleteLocalRef(cls);
if (!ret) {
LOGE("GeolocationPermissionsJni JNI: RegisterNatives fail.");
return false;
}
return true;
}
void GeolocationPermissionsJni::NativeInit(JNIEnv* env, jobject jobj)
{
LOGI("GeolocationPermissionsJni JNI: NativeInit");
CHECK_NULL_VOID(env);
g_webWebviewClass.globalRef = env->NewGlobalRef(jobj);
CHECK_NULL_VOID(g_webWebviewClass.globalRef);
jclass cls = env->GetObjectClass(jobj);
CHECK_NULL_VOID(cls);
g_webWebviewClass.allowGeolocation = env->GetMethodID(cls, METHOD_ALLOW_GEOLOCATION, SIGNATURE_ALLOW_GEOLOCATION);
g_webWebviewClass.deleteGeolocation = env->GetMethodID(cls, METHOD_DELETE_GEOLOCATION,
SIGNATURE_DELETE_GEOLOCATION);
g_webWebviewClass.deleteAllGeolocation = env->GetMethodID(cls, METHOD_DELETE_ALL_GEOLOCATION,
SIGNATURE_DELETE_ALL_GEOLOCATION);
g_webWebviewClass.getAccessibleGeolocation = env->GetMethodID(cls, METHOD_GET_ACCESSIBLE_GEOLOCATION,
SIGNATURE_GET_ACCESSIBLE_GEOLOCATION);
g_webWebviewClass.getStoredGeolocation = env->GetMethodID(cls, METHOD_GET_STORED_GEOLOCATION,
SIGNATURE_GET_STORED_GEOLOCATION);
env->DeleteLocalRef(cls);
}
void GeolocationPermissionsJni::AllowGeolocation(const std::string& origin, const bool incognito)
{
auto env = ARKUI_X_Plugin_GetJniEnv();
CHECK_NULL_VOID(env);
jstring jOrigin = env->NewStringUTF(origin.c_str());
CHECK_NULL_VOID(jOrigin);
env->CallVoidMethod(g_webWebviewClass.globalRef, g_webWebviewClass.allowGeolocation, jOrigin, true, incognito);
if (env->ExceptionCheck()) {
LOGE("GeolocationPermissionsJni JNI: call AllowGeolocation has exception");
env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(jOrigin);
return;
}
env->DeleteLocalRef(jOrigin);
}
void GeolocationPermissionsJni::DeleteGeolocation(const std::string& origin, const bool incognito)
{
auto env = ARKUI_X_Plugin_GetJniEnv();
CHECK_NULL_VOID(env);
jstring jOrigin = env->NewStringUTF(origin.c_str());
CHECK_NULL_VOID(jOrigin);
env->CallVoidMethod(g_webWebviewClass.globalRef, g_webWebviewClass.deleteGeolocation, jOrigin, incognito);
if (env->ExceptionCheck()) {
LOGE("GeolocationPermissionsJni JNI: call DeleteGeolocation has exception");
env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(jOrigin);
return;
}
env->DeleteLocalRef(jOrigin);
}
void GeolocationPermissionsJni::DeleteAllGeolocation(const bool incognito)
{
auto env = ARKUI_X_Plugin_GetJniEnv();
CHECK_NULL_VOID(env);
env->CallVoidMethod(g_webWebviewClass.globalRef, g_webWebviewClass.deleteAllGeolocation, incognito);
if (env->ExceptionCheck()) {
LOGE("GeolocationPermissionsJni JNI: call DeleteAllGeolocation has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
}
void GeolocationPermissionsJni::GetAccessibleGeolocation(const std::string& origin, int32_t asyncCallbackInfoId, const bool incognito)
{
auto env = ARKUI_X_Plugin_GetJniEnv();
CHECK_NULL_VOID(env);
jstring jOrigin = env->NewStringUTF(origin.c_str());
CHECK_NULL_VOID(jOrigin);
env->CallVoidMethod(g_webWebviewClass.globalRef, g_webWebviewClass.getAccessibleGeolocation, jOrigin, asyncCallbackInfoId, incognito);
if (env->ExceptionCheck()) {
LOGE("GeolocationPermissionsJni JNI: call GetAccessibleGeolocation has exception");
env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(jOrigin);
return;
}
env->DeleteLocalRef(jOrigin);
}
void GeolocationPermissionsJni::GetStoredGeolocation(int32_t asyncCallbackInfoId, const bool incognito)
{
auto env = ARKUI_X_Plugin_GetJniEnv();
CHECK_NULL_VOID(env);
env->CallVoidMethod(g_webWebviewClass.globalRef, g_webWebviewClass.getStoredGeolocation, asyncCallbackInfoId, incognito);
if (env->ExceptionCheck()) {
LOGE("GeolocationPermissionsJni JNI: call GetStoredGeolocation has exception");
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
}
void GeolocationPermissionsJni::OnReceiveGetAccessibleGeolocationValue(JNIEnv* env, jobject jobj, jboolean jResult, jint jId)
{
CHECK_NULL_VOID(env);
auto nativeId = static_cast<int32_t>(jId);
GeolocationPermissions::OnGetAccessibleReceiveValue(jResult, nativeId);
}
void GeolocationPermissionsJni::OnReceiveGetAccessibleGeolocationError(JNIEnv* env, jobject jobj, jstring jErrCode, jint jId)
{
CHECK_NULL_VOID(env);
CHECK_NULL_VOID(jErrCode);
std::string result;
int errcodeId = NWebError::NO_ERROR;
const char* content = env->GetStringUTFChars(jErrCode, nullptr);
if (content != nullptr) {
result.assign(content);
env->ReleaseStringUTFChars(jErrCode, content);
}
if (result.compare("INVALID_ORIGIN") == 0) {
errcodeId = NWebError::INVALID_ORIGIN;
}
auto nativeId = static_cast<int32_t>(jId);
GeolocationPermissions::OnGetAccessibleReceiveError(errcodeId, nativeId);
}
void GeolocationPermissionsJni::OnReceiveGetStoredGeolocationValue(JNIEnv* env, jobject jobj, jobjectArray jStringArray, jint jId)
{
CHECK_NULL_VOID(env);
CHECK_NULL_VOID(jStringArray);
std::vector<std::string> result;
jsize length = env->GetArrayLength(jStringArray);
if (length > 0) {
for (jsize i = 0; i < length; ++i) {
jstring jResult = static_cast<jstring>(env->GetObjectArrayElement(jStringArray, i));
const char* content = env->GetStringUTFChars(jResult, nullptr);
if (content != nullptr) {
result.push_back(std::string(content));
env->ReleaseStringUTFChars(jResult, content);
}
env->DeleteLocalRef(jResult);
}
}
auto nativeId = static_cast<int32_t>(jId);
GeolocationPermissions::OnGetStoredReceiveValue(result, nativeId);
}
}