LNXSDK/Kha/Backends/Kinc-HL/hl/src/std/sys_android.c

242 lines
8.1 KiB
C
Raw Normal View History

2025-01-22 16:18:30 +01:00
/*
* Copyright (C)2005-2018 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <hl.h>
#if defined(HL_MOBILE) && defined(HL_ANDROID)
#ifndef HL_ANDROID_ACTIVITY
# define HL_ANDROID_ACTIVITY "org/haxe/HashLinkActivity"
#endif
#include <jni.h>
#include <android/log.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef HL_JNI_LOG_TAG
#define HL_JNI_LOG_TAG "HL_JNI"
#endif
#define HL_ANDROID_EXTERNAL_STORAGE_NOT_MOUNTED (0)
#define HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RO (1)
#define HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RW (2 | HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RO)
/* #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,HL_JNI_LOG_TAG,__VA_ARGS__) */
/* #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,HL_JNI_LOG_TAG,__VA_ARGS__) */
#define LOGI(...) do {} while (0)
#define LOGE(...) do {} while (0)
/* Forward declaration for JNI thread management */
static void hl_android_jni_thread_destructor(void*);
/* pthread key for proper JVM thread handling */
static pthread_key_t hl_java_thread_key;
/* Java VM reference */
static JavaVM* hl_java_vm;
/* Main activity */
static jclass hl_java_activity_class;
/* Method signatures */
static jmethodID hl_java_method_id_get_context;
/* Paths */
static char *hl_android_external_files_path = NULL;
static char *hl_android_internal_files_path = NULL;
/* Function to retrieve JNI environment, and dealing with threading */
static JNIEnv* hl_android_jni_get_env(void)
{
/* Always try to attach if calling from a non-attached thread */
JNIEnv *env;
if((*hl_java_vm)->AttachCurrentThread(hl_java_vm, &env, NULL) < 0) {
LOGE("failed to attach current thread");
return 0;
}
pthread_setspecific(hl_java_thread_key, (void*) env);
return env;
}
/* JNI_OnLoad is automatically called when loading shared library through System.loadLibrary() Java call */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv *env;
jclass cls;
hl_java_vm = vm;
if ((*hl_java_vm)->GetEnv(hl_java_vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Failed to get the environment using GetEnv()");
return -1;
}
/* Create pthread "destructor" pthread key to detach properly all threads */
if (pthread_key_create(&hl_java_thread_key, hl_android_jni_thread_destructor) != 0) {
__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Error initializing pthread key");
}
/* Make sure we are attached (we should) and setup pthread destructor */
env = hl_android_jni_get_env();
/* Try to retrieve local reference to our Activity class */
cls = (*env)->FindClass(env, HL_ANDROID_ACTIVITY);
if (!cls) {
__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Error cannot find HashLink Activity class");
}
/* Create a global reference for our Activity class */
hl_java_activity_class = (jclass)((*env)->NewGlobalRef(env, cls));
/* Retrieve the getContext() method id */
hl_java_method_id_get_context = (*env)->GetStaticMethodID(env, hl_java_activity_class, "getContext","()Landroid/content/Context;");
if (!hl_java_method_id_get_context) {
__android_log_print(ANDROID_LOG_ERROR, HL_JNI_LOG_TAG, "Error cannot get getContext() method on specified Activity class (not an Activity ?)");
}
return JNI_VERSION_1_4;
}
static void hl_android_jni_thread_destructor(void* value)
{
/* The thread is being destroyed, detach it from the Java VM and set the hl_java_thread_key value to NULL as required */
JNIEnv *env = (JNIEnv*) value;
if (env != NULL) {
(*hl_java_vm)->DetachCurrentThread(hl_java_vm);
pthread_setspecific(hl_java_thread_key, NULL);
}
}
static int hl_sys_android_get_external_storage_state(void)
{
jmethodID mid;
jclass cls;
jstring stateString;
const char *state;
int status = HL_ANDROID_EXTERNAL_STORAGE_NOT_MOUNTED;
JNIEnv *env = hl_android_jni_get_env();
if (!env) {
LOGE("Couldn't get Android JNIEnv !");
return status;
}
cls = (*env)->FindClass(env, "android/os/Environment");
mid = (*env)->GetStaticMethodID(env, cls, "getExternalStorageState", "()Ljava/lang/String;");
stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
if (!stateString) {
LOGE("Call to getExternalStorageState failed");
return status;
}
state = (*env)->GetStringUTFChars(env, stateString, NULL);
if (strcmp(state, "mounted") == 0) {
status = HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RW;
} else if (strcmp(state, "mounted_ro") == 0) {
status = HL_ANDROID_EXTERNAL_STORAGE_MOUNTED_RO;
}
(*env)->ReleaseStringUTFChars(env, stateString, state);
return status;
}
static char* hl_sys_android_get_absolute_path_from(const char* method, const char* signature)
{
jmethodID mid;
jobject context;
jobject fileObject;
jstring pathString;
const char *path;
char* retrievedPath = NULL;
JNIEnv *env = hl_android_jni_get_env();
if (!env) {
LOGE("Couldn't get Android JNIEnv !");
return NULL;
}
context = (*env)->CallStaticObjectMethod(env, hl_java_activity_class, hl_java_method_id_get_context);
if (!context) {
LOGE("Couldn't get Android context!");
return NULL;
}
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), method, signature);
fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
if (!fileObject) {
LOGE("Couldn't call %s%s on the specified context", method, signature);
return NULL;
}
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject), "getAbsolutePath", "()Ljava/lang/String;");
pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
if (!pathString) {
LOGE("Couldn't retrieve absolute path");
return NULL;
}
/* Retrieve as C string */
path = (*env)->GetStringUTFChars(env, pathString, NULL);
retrievedPath = strdup(path);
(*env)->ReleaseStringUTFChars(env, pathString, path);
return retrievedPath;
}
static const char* hl_sys_android_get_external_storage_path(void)
{
/* Make sure external storage is mounted (at least read-only) */
if (hl_sys_android_get_external_storage_state()==HL_ANDROID_EXTERNAL_STORAGE_NOT_MOUNTED)
return NULL;
if (!hl_android_external_files_path) {
hl_android_external_files_path = hl_sys_android_get_absolute_path_from("getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
}
return hl_android_external_files_path;
}
static const char* hl_sys_android_get_internal_storage_path(void)
{
/* Internal storage is always available */
if (!hl_android_internal_files_path) {
hl_android_internal_files_path = hl_sys_android_get_absolute_path_from("getFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
}
return hl_android_internal_files_path;
}
const char *hl_sys_special( const char *key ) {
if (strcmp(key, "android_external_storage_path")==0)
return hl_sys_android_get_external_storage_path();
else if (strcmp(key, "android_internal_storage_path")==0)
return hl_sys_android_get_internal_storage_path();
else
hl_error("Unknown sys_special key");
return NULL;
}
DEFINE_PRIM(_BYTES, sys_special, _BYTES);
#endif