#if defined(ANDROID) || defined(__ANDROID__)
# include "ares_private.h"
# include <jni.h>
# include <sys/prctl.h>
# include "ares_android.h"
static
JavaVM *android_jvm = NULL;
static
jobject android_connectivity_manager = NULL;
static
jmethodID android_cm_active_net_mid = NULL;
static
jmethodID android_cm_link_props_mid = NULL;
static
jmethodID android_lp_dns_servers_mid = NULL;
static
jmethodID android_lp_domains_mid = NULL;
static
jmethodID android_list_size_mid = NULL;
static
jmethodID android_list_get_mid = NULL;
static
jmethodID android_ia_host_addr_mid = NULL;
static
jclass jni_get_class(JNIEnv *env,
const
char
*path)
{
jclass cls = NULL;
if
(env == NULL || path == NULL || *path ==
'\0'
) {
return
NULL;
}
cls = (*env)->FindClass(env, path);
if
((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionClear(env);
return
NULL;
}
return
cls;
}
static
jmethodID jni_get_method_id(JNIEnv *env, jclass cls,
const
char
*func_name,
const
char
*signature)
{
jmethodID mid = NULL;
if
(env == NULL || cls == NULL || func_name == NULL || *func_name ==
'\0'
||
signature == NULL || *signature ==
'\0'
) {
return
NULL;
}
mid = (*env)->GetMethodID(env, cls, func_name, signature);
if
((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionClear(env);
return
NULL;
}
return
mid;
}
static
int
jvm_attach(JNIEnv **env)
{
char
name[17] = { 0 };
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6;
if
(prctl(PR_GET_NAME, name) == 0) {
args.name = name;
}
else
{
args.name = NULL;
}
args.group = NULL;
return
(*android_jvm)->AttachCurrentThread(android_jvm, env, &args);
}
void
ares_library_init_jvm(JavaVM *jvm)
{
android_jvm = jvm;
}
int
ares_library_init_android(jobject connectivity_manager)
{
JNIEnv *env = NULL;
int
need_detatch = 0;
int
res;
ares_status_t ret = ARES_ENOTINITIALIZED;
jclass obj_cls = NULL;
if
(android_jvm == NULL) {
goto
cleanup;
}
res = (*android_jvm)->GetEnv(android_jvm, (
void
**)&env, JNI_VERSION_1_6);
if
(res == JNI_EDETACHED) {
env = NULL;
res = jvm_attach(&env);
need_detatch = 1;
}
if
(res != JNI_OK || env == NULL) {
goto
cleanup;
}
android_connectivity_manager =
(*env)->NewGlobalRef(env, connectivity_manager);
if
(android_connectivity_manager == NULL) {
goto
cleanup;
}
ret = ARES_SUCCESS;
obj_cls = jni_get_class(env,
"android/net/ConnectivityManager"
);
if
(obj_cls == NULL) {
goto
cleanup;
}
android_cm_active_net_mid = jni_get_method_id(
env, obj_cls,
"getActiveNetwork"
,
"()Landroid/net/Network;"
);
if
(android_cm_active_net_mid == NULL) {
goto
cleanup;
}
android_cm_link_props_mid =
jni_get_method_id(env, obj_cls,
"getLinkProperties"
,
"(Landroid/net/Network;)Landroid/net/LinkProperties;"
);
if
(android_cm_link_props_mid == NULL) {
goto
cleanup;
}
(*env)->DeleteLocalRef(env, obj_cls);
obj_cls = jni_get_class(env,
"android/net/LinkProperties"
);
if
(obj_cls == NULL) {
goto
cleanup;
}
android_lp_dns_servers_mid =
jni_get_method_id(env, obj_cls,
"getDnsServers"
,
"()Ljava/util/List;"
);
if
(android_lp_dns_servers_mid == NULL) {
goto
cleanup;
}
android_lp_domains_mid =
jni_get_method_id(env, obj_cls,
"getDomains"
,
"()Ljava/lang/String;"
);
if
(android_lp_domains_mid == NULL) {
goto
cleanup;
}
(*env)->DeleteLocalRef(env, obj_cls);
obj_cls = jni_get_class(env,
"java/util/List"
);
if
(obj_cls == NULL) {
goto
cleanup;
}
android_list_size_mid = jni_get_method_id(env, obj_cls,
"size"
,
"()I"
);
if
(android_list_size_mid == NULL) {
goto
cleanup;
}
android_list_get_mid =
jni_get_method_id(env, obj_cls,
"get"
,
"(I)Ljava/lang/Object;"
);
if
(android_list_get_mid == NULL) {
goto
cleanup;
}
(*env)->DeleteLocalRef(env, obj_cls);
obj_cls = jni_get_class(env,
"java/net/InetAddress"
);
if
(obj_cls == NULL) {
goto
cleanup;
}
android_ia_host_addr_mid =
jni_get_method_id(env, obj_cls,
"getHostAddress"
,
"()Ljava/lang/String;"
);
if
(android_ia_host_addr_mid == NULL) {
goto
cleanup;
}
(*env)->DeleteLocalRef(env, obj_cls);
goto
done;
cleanup:
if
(obj_cls != NULL) {
(*env)->DeleteLocalRef(env, obj_cls);
}
android_cm_active_net_mid = NULL;
android_cm_link_props_mid = NULL;
android_lp_dns_servers_mid = NULL;
android_lp_domains_mid = NULL;
android_list_size_mid = NULL;
android_list_get_mid = NULL;
android_ia_host_addr_mid = NULL;
done:
if
(need_detatch) {
(*android_jvm)->DetachCurrentThread(android_jvm);
}
return
(
int
)ret;
}
int
ares_library_android_initialized(
void
)
{
if
(android_jvm == NULL || android_connectivity_manager == NULL) {
return
ARES_ENOTINITIALIZED;
}
return
ARES_SUCCESS;
}
void
ares_library_cleanup_android(
void
)
{
JNIEnv *env = NULL;
int
need_detatch = 0;
int
res;
if
(android_jvm == NULL || android_connectivity_manager == NULL) {
return
;
}
res = (*android_jvm)->GetEnv(android_jvm, (
void
**)&env, JNI_VERSION_1_6);
if
(res == JNI_EDETACHED) {
env = NULL;
res = jvm_attach(&env);
need_detatch = 1;
}
if
(res != JNI_OK || env == NULL) {
return
;
}
android_cm_active_net_mid = NULL;
android_cm_link_props_mid = NULL;
android_lp_dns_servers_mid = NULL;
android_lp_domains_mid = NULL;
android_list_size_mid = NULL;
android_list_get_mid = NULL;
android_ia_host_addr_mid = NULL;
(*env)->DeleteGlobalRef(env, android_connectivity_manager);
android_connectivity_manager = NULL;
if
(need_detatch) {
(*android_jvm)->DetachCurrentThread(android_jvm);
}
}
char
**ares_get_android_server_list(
size_t
max_servers,
size_t
*num_servers)
{
JNIEnv *env = NULL;
jobject active_network = NULL;
jobject link_properties = NULL;
jobject server_list = NULL;
jobject server = NULL;
jstring str = NULL;
jint nserv;
const
char
*ch_server_address;
int
res;
size_t
i;
char
**dns_list = NULL;
int
need_detatch = 0;
if
(android_jvm == NULL || android_connectivity_manager == NULL ||
max_servers == 0 || num_servers == NULL) {
return
NULL;
}
if
(android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL ||
android_lp_dns_servers_mid == NULL || android_list_size_mid == NULL ||
android_list_get_mid == NULL || android_ia_host_addr_mid == NULL) {
return
NULL;
}
res = (*android_jvm)->GetEnv(android_jvm, (
void
**)&env, JNI_VERSION_1_6);
if
(res == JNI_EDETACHED) {
env = NULL;
res = jvm_attach(&env);
need_detatch = 1;
}
if
(res != JNI_OK || env == NULL) {
goto
done;
}
active_network = (*env)->CallObjectMethod(env, android_connectivity_manager,
android_cm_active_net_mid);
if
(active_network == NULL) {
goto
done;
}
link_properties =
(*env)->CallObjectMethod(env, android_connectivity_manager,
android_cm_link_props_mid, active_network);
if
(link_properties == NULL) {
goto
done;
}
server_list =
(*env)->CallObjectMethod(env, link_properties, android_lp_dns_servers_mid);
if
(server_list == NULL) {
goto
done;
}
nserv = (*env)->CallIntMethod(env, server_list, android_list_size_mid);
if
(nserv > (jint)max_servers) {
nserv = (jint)max_servers;
}
if
(nserv <= 0) {
goto
done;
}
*num_servers = (
size_t
)nserv;
dns_list = ares_malloc(
sizeof
(*dns_list) * (*num_servers));
for
(i = 0; i < *num_servers; i++) {
size_t
len = 64;
server =
(*env)->CallObjectMethod(env, server_list, android_list_get_mid, (jint)i);
dns_list[i] = ares_malloc(len);
dns_list[i][0] = 0;
if
(server == NULL) {
continue
;
}
str = (*env)->CallObjectMethod(env, server, android_ia_host_addr_mid);
ch_server_address = (*env)->GetStringUTFChars(env, str, 0);
ares_strcpy(dns_list[i], ch_server_address, len);
(*env)->ReleaseStringUTFChars(env, str, ch_server_address);
(*env)->DeleteLocalRef(env, str);
(*env)->DeleteLocalRef(env, server);
}
done:
if
((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionClear(env);
}
if
(server_list != NULL) {
(*env)->DeleteLocalRef(env, server_list);
}
if
(link_properties != NULL) {
(*env)->DeleteLocalRef(env, link_properties);
}
if
(active_network != NULL) {
(*env)->DeleteLocalRef(env, active_network);
}
if
(need_detatch) {
(*android_jvm)->DetachCurrentThread(android_jvm);
}
return
dns_list;
}
char
*ares_get_android_search_domains_list(
void
)
{
JNIEnv *env = NULL;
jobject active_network = NULL;
jobject link_properties = NULL;
jstring domains = NULL;
const
char
*domain;
int
res;
char
*domain_list = NULL;
int
need_detatch = 0;
if
(android_jvm == NULL || android_connectivity_manager == NULL) {
return
NULL;
}
if
(android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL ||
android_lp_domains_mid == NULL) {
return
NULL;
}
res = (*android_jvm)->GetEnv(android_jvm, (
void
**)&env, JNI_VERSION_1_6);
if
(res == JNI_EDETACHED) {
env = NULL;
res = jvm_attach(&env);
need_detatch = 1;
}
if
(res != JNI_OK || env == NULL) {
goto
done;
}
active_network = (*env)->CallObjectMethod(env, android_connectivity_manager,
android_cm_active_net_mid);
if
(active_network == NULL) {
goto
done;
}
link_properties =
(*env)->CallObjectMethod(env, android_connectivity_manager,
android_cm_link_props_mid, active_network);
if
(link_properties == NULL) {
goto
done;
}
domains =
(*env)->CallObjectMethod(env, link_properties, android_lp_domains_mid);
if
(domains == NULL) {
goto
done;
}
domain = (*env)->GetStringUTFChars(env, domains, 0);
domain_list = ares_strdup(domain);
(*env)->ReleaseStringUTFChars(env, domains, domain);
(*env)->DeleteLocalRef(env, domains);
done:
if
((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionClear(env);
}
if
(link_properties != NULL) {
(*env)->DeleteLocalRef(env, link_properties);
}
if
(active_network != NULL) {
(*env)->DeleteLocalRef(env, active_network);
}
if
(need_detatch) {
(*android_jvm)->DetachCurrentThread(android_jvm);
}
return
domain_list;
}
#else
typedef
int
dummy_make_iso_compilers_happy;
#endif