aboutsummaryrefslogtreecommitdiffstats
path: root/src/frontends/android/jni/libandroidbridge
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontends/android/jni/libandroidbridge')
-rw-r--r--src/frontends/android/jni/libandroidbridge/Android.mk1
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.c54
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.h5
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.c58
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.h11
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c3
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_net.c77
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.c244
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.h91
9 files changed, 493 insertions, 51 deletions
diff --git a/src/frontends/android/jni/libandroidbridge/Android.mk b/src/frontends/android/jni/libandroidbridge/Android.mk
index f47e39656..b1fa4d1cb 100644
--- a/src/frontends/android/jni/libandroidbridge/Android.mk
+++ b/src/frontends/android/jni/libandroidbridge/Android.mk
@@ -11,6 +11,7 @@ backend/android_service.c backend/android_service.h \
charonservice.c charonservice.h \
kernel/android_ipsec.c kernel/android_ipsec.h \
kernel/android_net.c kernel/android_net.h \
+kernel/network_manager.c kernel/network_manager.h \
vpnservice_builder.c vpnservice_builder.h
# build libandroidbridge -------------------------------------------------------
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
index 2a115d2f9..b00567f60 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
@@ -59,11 +59,6 @@ struct private_android_service_t {
char *type;
/**
- * local ipv4 address
- */
- char *local_address;
-
- /**
* gateway
*/
char *gateway;
@@ -362,7 +357,6 @@ METHOD(listener_t, child_updown, bool,
{
/* disable the hooks registered to catch initiation failures */
this->public.listener.ike_updown = NULL;
- this->public.listener.ike_state_change = NULL;
if (!setup_tun_device(this, ike_sa, child_sa))
{
DBG1(DBG_DMN, "failed to setup TUN device");
@@ -403,19 +397,6 @@ METHOD(listener_t, ike_updown, bool,
return TRUE;
}
-METHOD(listener_t, ike_state_change, bool,
- private_android_service_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
-{
- /* this call back is only registered during initiation */
- if (this->ike_sa == ike_sa && state == IKE_DESTROYING)
- {
- charonservice->update_status(charonservice,
- CHARONSERVICE_UNREACHABLE_ERROR);
- return FALSE;
- }
- return TRUE;
-}
-
METHOD(listener_t, alert, bool,
private_android_service_t *this, ike_sa_t *ike_sa, alert_t alert,
va_list args)
@@ -432,6 +413,15 @@ METHOD(listener_t, alert, bool,
charonservice->update_status(charonservice,
CHARONSERVICE_PEER_AUTH_ERROR);
break;
+ case ALERT_PEER_INIT_UNREACHABLE:
+ this->lock->read_lock(this->lock);
+ if (this->tunfd < 0)
+ { /* only handle this if we are not reestablishing the SA */
+ charonservice->update_status(charonservice,
+ CHARONSERVICE_UNREACHABLE_ERROR);
+ }
+ this->lock->unlock(this->lock);
+ break;
default:
break;
}
@@ -455,9 +445,8 @@ METHOD(listener_t, ike_reestablish, bool,
if (this->ike_sa == old)
{
this->ike_sa = new;
- /* re-register hooks to detect initiation failures */
+ /* re-register hook to detect initiation failures */
this->public.listener.ike_updown = _ike_updown;
- this->public.listener.ike_state_change = _ike_state_change;
/* the TUN device will be closed when the new CHILD_SA is established */
}
return TRUE;
@@ -480,13 +469,13 @@ static job_requeue_t initiate(private_android_service_t *this)
}
};
- ike_cfg = ike_cfg_create(TRUE, TRUE, this->local_address, FALSE,
+ ike_cfg = ike_cfg_create(TRUE, TRUE, "0.0.0.0", FALSE,
charon->socket->get_port(charon->socket, FALSE),
this->gateway, FALSE, IKEV2_UDP_PORT);
ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
peer_cfg = peer_cfg_create("android", IKEV2, ike_cfg, CERT_SEND_IF_ASKED,
- UNIQUE_REPLACE, 1, /* keyingtries */
+ UNIQUE_REPLACE, 0, /* keyingtries */
36000, 0, /* rekey 10h, reauth none */
600, 600, /* jitter, over 10min */
TRUE, FALSE, /* mobike, aggressive */
@@ -538,10 +527,14 @@ static job_requeue_t initiate(private_android_service_t *this)
peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL,
- ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
- 0, 0, NULL, NULL, 0);
- child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
- ts = traffic_selector_create_dynamic(0, 0, 65535);
+ ACTION_NONE, ACTION_RESTART, ACTION_RESTART,
+ FALSE, 0, 0, NULL, NULL, 0);
+ /* create an ESP proposal with the algorithms currently supported by
+ * libipsec, no PFS for now */
+ child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP,
+ "aes128-aes192-aes256-sha1-sha256-sha384-sha512"));
+ ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0",
+ 0, "255.255.255.255", 65535);
child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0",
0, "255.255.255.255", 65535);
@@ -588,7 +581,6 @@ METHOD(android_service_t, destroy, void,
close_tun_device(this);
this->lock->destroy(this->lock);
free(this->type);
- free(this->local_address);
free(this->gateway);
free(this->username);
if (this->password)
@@ -603,8 +595,8 @@ METHOD(android_service_t, destroy, void,
* See header
*/
android_service_t *android_service_create(android_creds_t *creds, char *type,
- char *local_address, char *gateway,
- char *username, char *password)
+ char *gateway, char *username,
+ char *password)
{
private_android_service_t *this;
@@ -614,14 +606,12 @@ android_service_t *android_service_create(android_creds_t *creds, char *type,
.ike_rekey = _ike_rekey,
.ike_reestablish = _ike_reestablish,
.ike_updown = _ike_updown,
- .ike_state_change = _ike_state_change,
.child_updown = _child_updown,
.alert = _alert,
},
.destroy = _destroy,
},
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
- .local_address = local_address,
.username = username,
.password = password,
.gateway = gateway,
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.h b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
index 52c3dc5c8..1bfdcf994 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.h
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
@@ -53,13 +53,12 @@ struct android_service_t {
*
* @param creds Android specific credential set
* @param type VPN type (see VpnType.java)
- * @param local_address local ip address
* @param gateway gateway address
* @param username user name (local identity)
* @param password password (if any)
*/
android_service_t *android_service_create(android_creds_t *creds, char *type,
- char *local_address, char *gateway,
- char *username, char *password);
+ char *gateway, char *username,
+ char *password);
#endif /** ANDROID_SERVICE_H_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c
index ef4d42edf..1fde9887b 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.c
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.c
@@ -38,7 +38,7 @@
#define ANDROID_DEBUG_LEVEL 1
#define ANDROID_RETRASNMIT_TRIES 3
-#define ANDROID_RETRANSMIT_TIMEOUT 3.0
+#define ANDROID_RETRANSMIT_TIMEOUT 2.0
#define ANDROID_RETRANSMIT_BASE 1.4
typedef struct private_charonservice_t private_charonservice_t;
@@ -74,9 +74,19 @@ struct private_charonservice_t {
vpnservice_builder_t *builder;
/**
+ * NetworkManager instance (accessed via JNI)
+ */
+ network_manager_t *network_manager;
+
+ /**
* CharonVpnService reference
*/
jobject vpn_service;
+
+ /**
+ * Sockets that were bypassed and we keep track for
+ */
+ linked_list_t *sockets;
};
/**
@@ -172,8 +182,10 @@ failed:
return success;
}
-METHOD(charonservice_t, bypass_socket, bool,
- private_charonservice_t *this, int fd, int family)
+/**
+ * Bypass a single socket
+ */
+static bool bypass_single_socket(intptr_t fd, private_charonservice_t *this)
{
JNIEnv *env;
jmethodID method_id;
@@ -188,7 +200,7 @@ METHOD(charonservice_t, bypass_socket, bool,
}
if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd))
{
- DBG1(DBG_CFG, "VpnService.protect() failed");
+ DBG2(DBG_KNL, "VpnService.protect() failed");
goto failed;
}
androidjni_detach_thread();
@@ -200,6 +212,19 @@ failed:
return FALSE;
}
+METHOD(charonservice_t, bypass_socket, bool,
+ private_charonservice_t *this, int fd, int family)
+{
+ if (fd >= 0)
+ {
+ this->sockets->insert_last(this->sockets, (void*)(intptr_t)fd);
+ return bypass_single_socket((intptr_t)fd, this);
+ }
+ this->sockets->invoke_function(this->sockets, (void*)bypass_single_socket,
+ this);
+ return TRUE;
+}
+
/**
* Converts the given Java array of byte arrays (byte[][]) to a linked list
* of chunk_t objects.
@@ -330,22 +355,26 @@ METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*,
return this->builder;
}
+METHOD(charonservice_t, get_network_manager, network_manager_t*,
+ private_charonservice_t *this)
+{
+ return this->network_manager;
+}
+
/**
* Initiate a new connection
*
- * @param local local ip address (gets owned)
* @param gateway gateway address (gets owned)
* @param username username (gets owned)
* @param password password (gets owned)
*/
-static void initiate(char *type, char *local, char *gateway,
- char *username, char *password)
+static void initiate(char *type, char *gateway, char *username, char *password)
{
private_charonservice_t *this = (private_charonservice_t*)charonservice;
this->creds->clear(this->creds);
DESTROY_IF(this->service);
- this->service = android_service_create(this->creds, type, local, gateway,
+ this->service = android_service_create(this->creds, type, gateway,
username, password);
}
@@ -400,10 +429,13 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
.get_user_certificate = _get_user_certificate,
.get_user_key = _get_user_key,
.get_vpnservice_builder = _get_vpnservice_builder,
+ .get_network_manager = _get_network_manager,
},
.attr = android_attr_create(),
.creds = android_creds_create(),
.builder = vpnservice_builder_create(builder),
+ .network_manager = network_manager_create(service),
+ .sockets = linked_list_create(),
.vpn_service = (*env)->NewGlobalRef(env, service),
);
charonservice = &this->public;
@@ -439,6 +471,8 @@ static void charonservice_deinit(JNIEnv *env)
{
private_charonservice_t *this = (private_charonservice_t*)charonservice;
+ this->network_manager->destroy(this->network_manager);
+ this->sockets->destroy(this->sockets);
this->builder->destroy(this->builder);
this->creds->destroy(this->creds);
this->attr->destroy(this->attr);
@@ -555,16 +589,14 @@ JNI_METHOD(CharonVpnService, deinitializeCharon, void)
* Initiate SA
*/
JNI_METHOD(CharonVpnService, initiate, void,
- jstring jtype, jstring jlocal_address, jstring jgateway, jstring jusername,
- jstring jpassword)
+ jstring jtype, jstring jgateway, jstring jusername, jstring jpassword)
{
- char *type, *local_address, *gateway, *username, *password;
+ char *type, *gateway, *username, *password;
type = androidjni_convert_jstring(env, jtype);
- local_address = androidjni_convert_jstring(env, jlocal_address);
gateway = androidjni_convert_jstring(env, jgateway);
username = androidjni_convert_jstring(env, jusername);
password = androidjni_convert_jstring(env, jpassword);
- initiate(type, local_address, gateway, username, password);
+ initiate(type, gateway, username, password);
}
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.h b/src/frontends/android/jni/libandroidbridge/charonservice.h
index 376f55014..4b5839f5e 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.h
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.h
@@ -32,6 +32,7 @@
#define CHARONSERVICE_H_
#include "vpnservice_builder.h"
+#include "kernel/network_manager.h"
#include <library.h>
#include <utils/linked_list.h>
@@ -69,7 +70,9 @@ struct charonservice_t {
/**
* Install a bypass policy for the given socket using the protect() Method
- * of the Android VpnService interface
+ * of the Android VpnService interface.
+ *
+ * Use -1 as fd to re-bypass previously bypassed sockets.
*
* @param fd socket file descriptor
* @param family socket protocol family
@@ -111,6 +114,12 @@ struct charonservice_t {
*/
vpnservice_builder_t *(*get_vpnservice_builder)(charonservice_t *this);
+ /**
+ * Get the current network_manager_t object
+ *
+ * @return NetworkManager instance
+ */
+ network_manager_t *(*get_network_manager)(charonservice_t *this);
};
/**
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c
index 08cc61610..85fe5d4c1 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c
@@ -79,7 +79,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
bool encap, bool new_encap, mark_t mark)
{
- return NOT_SUPPORTED;
+ return ipsec->sas->update_sa(ipsec->sas, spi, protocol, cpi, src, dst,
+ new_src, new_dst, encap, new_encap, mark);
}
METHOD(kernel_ipsec_t, query_sa, status_t,
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
index e29f95510..430c95bc8 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
@@ -14,6 +14,15 @@
#include "android_net.h"
+#include "../charonservice.h"
+#include <hydra.h>
+#include <debug.h>
+#include <processing/jobs/callback_job.h>
+#include <threading/mutex.h>
+
+/** delay before firing roam events (ms) */
+#define ROAM_DELAY 100
+
typedef struct private_kernel_android_net_t private_kernel_android_net_t;
struct private_kernel_android_net_t {
@@ -22,8 +31,66 @@ struct private_kernel_android_net_t {
* Public kernel interface
*/
kernel_android_net_t public;
+
+ /**
+ * Reference to NetworkManager object
+ */
+ network_manager_t *network_manager;
+
+ /**
+ * earliest time of the next roam event
+ */
+ timeval_t next_roam;
+
+ /**
+ * mutex to check and update roam event time
+ */
+ mutex_t *mutex;
};
+/**
+ * callback function that raises the delayed roam event
+ */
+static job_requeue_t roam_event()
+{
+ /* this will fail if no connection is up */
+ charonservice->bypass_socket(charonservice, -1, 0);
+ hydra->kernel_interface->roam(hydra->kernel_interface, TRUE);
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Listen for connectivity change events and queue a roam event
+ */
+static void connectivity_cb(private_kernel_android_net_t *this,
+ bool disconnected)
+{
+ timeval_t now;
+ job_t *job;
+
+ time_monotonic(&now);
+ this->mutex->lock(this->mutex);
+ if (!timercmp(&now, &this->next_roam, >))
+ {
+ this->mutex->unlock(this->mutex);
+ return;
+ }
+ timeval_add_ms(&now, ROAM_DELAY);
+ this->next_roam = now;
+ this->mutex->unlock(this->mutex);
+
+ job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, NULL,
+ NULL, NULL);
+ lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
+}
+
+METHOD(kernel_net_t, get_source_addr, host_t*,
+ private_kernel_android_net_t *this, host_t *dest, host_t *src)
+{
+ return this->network_manager->get_local_address(this->network_manager,
+ dest->get_family(dest) == AF_INET);
+}
+
METHOD(kernel_net_t, add_ip, status_t,
private_kernel_android_net_t *this, host_t *virtual_ip, host_t *iface_ip)
{
@@ -34,6 +101,9 @@ METHOD(kernel_net_t, add_ip, status_t,
METHOD(kernel_net_t, destroy, void,
private_kernel_android_net_t *this)
{
+ this->network_manager->remove_connectivity_cb(this->network_manager,
+ (void*)connectivity_cb);
+ this->mutex->destroy(this->mutex);
free(this);
}
@@ -47,7 +117,7 @@ kernel_android_net_t *kernel_android_net_create()
INIT(this,
.public = {
.interface = {
- .get_source_addr = (void*)return_null,
+ .get_source_addr = _get_source_addr,
.get_nexthop = (void*)return_null,
.get_interface = (void*)return_null,
.create_address_enumerator = (void*)enumerator_create_empty,
@@ -58,7 +128,12 @@ kernel_android_net_t *kernel_android_net_create()
.destroy = _destroy,
},
},
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .network_manager = charonservice->get_network_manager(charonservice),
);
+ timerclear(&this->next_roam);
+ this->network_manager->add_connectivity_cb(this->network_manager,
+ (void*)connectivity_cb, this);
return &this->public;
};
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
new file mode 100644
index 000000000..9c97fbb14
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "network_manager.h"
+
+#include "../android_jni.h"
+#include "../charonservice.h"
+#include <debug.h>
+#include <threading/mutex.h>
+
+typedef struct private_network_manager_t private_network_manager_t;
+
+struct private_network_manager_t {
+
+ /**
+ * Public interface
+ */
+ network_manager_t public;
+
+ /**
+ * Reference to NetworkManager object
+ */
+ jobject obj;
+
+ /**
+ * Java class for NetworkManager
+ */
+ jclass cls;
+
+ /**
+ * Registered callback
+ */
+ struct {
+ connectivity_cb_t cb;
+ void *data;
+ } connectivity_cb;
+
+ /**
+ * Mutex to access callback
+ */
+ mutex_t *mutex;
+};
+
+METHOD(network_manager_t, get_local_address, host_t*,
+ private_network_manager_t *this, bool ipv4)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+ jstring jaddr;
+ char *addr;
+ host_t *host;
+
+ androidjni_attach_thread(&env);
+ method_id = (*env)->GetMethodID(env, this->cls, "getLocalAddress",
+ "(Z)Ljava/lang/String;");
+ if (!method_id)
+ {
+ goto failed;
+ }
+ jaddr = (*env)->CallObjectMethod(env, this->obj, method_id, ipv4);
+ if (!jaddr)
+ {
+ goto failed;
+ }
+ addr = androidjni_convert_jstring(env, jaddr);
+ androidjni_detach_thread();
+ host = host_create_from_string(addr, 0);
+ free(addr);
+ return host;
+
+failed:
+ androidjni_exception_occurred(env);
+ androidjni_detach_thread();
+ return NULL;
+}
+
+JNI_METHOD(NetworkManager, networkChanged, void,
+ bool disconnected)
+{
+ private_network_manager_t *nm;
+
+ nm = (private_network_manager_t*)charonservice->get_network_manager(
+ charonservice);
+ nm->mutex->lock(nm->mutex);
+ if (nm->connectivity_cb.cb)
+ {
+ nm->connectivity_cb.cb(nm->connectivity_cb.data, disconnected);
+ }
+ nm->mutex->unlock(nm->mutex);
+}
+
+METHOD(network_manager_t, add_connectivity_cb, void,
+ private_network_manager_t *this, connectivity_cb_t cb, void *data)
+{
+ this->mutex->lock(this->mutex);
+ if (!this->connectivity_cb.cb)
+ {
+ JNIEnv *env;
+ jmethodID method_id;
+
+ androidjni_attach_thread(&env);
+ method_id = (*env)->GetMethodID(env, this->cls, "Register", "()V");
+ if (!method_id)
+ {
+ androidjni_exception_occurred(env);
+ }
+ else
+ {
+ (*env)->CallVoidMethod(env, this->obj, method_id);
+ if (!androidjni_exception_occurred(env))
+ {
+ this->connectivity_cb.cb = cb;
+ this->connectivity_cb.data = data;
+ }
+ androidjni_detach_thread();
+ }
+ }
+ this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Unregister the NetworkManager via JNI.
+ *
+ * this->mutex has to be locked
+ */
+static void unregister_network_manager(private_network_manager_t *this)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+
+ androidjni_attach_thread(&env);
+ method_id = (*env)->GetMethodID(env, this->cls, "Unregister", "()V");
+ if (!method_id)
+ {
+ androidjni_exception_occurred(env);
+ }
+ else
+ {
+ (*env)->CallVoidMethod(env, this->obj, method_id);
+ androidjni_exception_occurred(env);
+ }
+ androidjni_detach_thread();
+}
+
+METHOD(network_manager_t, remove_connectivity_cb, void,
+ private_network_manager_t *this, connectivity_cb_t cb)
+{
+ this->mutex->lock(this->mutex);
+ if (this->connectivity_cb.cb == cb)
+ {
+ this->connectivity_cb.cb = NULL;
+ unregister_network_manager(this);
+ }
+ this->mutex->unlock(this->mutex);
+}
+
+METHOD(network_manager_t, destroy, void,
+ private_network_manager_t *this)
+{
+ JNIEnv *env;
+
+ this->mutex->lock(this->mutex);
+ if (this->connectivity_cb.cb)
+ {
+ this->connectivity_cb.cb = NULL;
+ unregister_network_manager(this);
+ }
+ this->mutex->unlock(this->mutex);
+
+ androidjni_attach_thread(&env);
+ if (this->obj)
+ {
+ (*env)->DeleteGlobalRef(env, this->obj);
+ }
+ if (this->cls)
+ {
+ (*env)->DeleteGlobalRef(env, this->cls);
+ }
+ androidjni_detach_thread();
+ this->mutex->destroy(this->mutex);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+network_manager_t *network_manager_create(jobject context)
+{
+ private_network_manager_t *this;
+ JNIEnv *env;
+ jmethodID method_id;
+ jobject obj;
+ jclass cls;
+
+ INIT(this,
+ .public = {
+ .get_local_address = _get_local_address,
+ .add_connectivity_cb = _add_connectivity_cb,
+ .remove_connectivity_cb = _remove_connectivity_cb,
+ .destroy = _destroy,
+ },
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ androidjni_attach_thread(&env);
+ cls = (*env)->FindClass(env, JNI_PACKAGE_STRING "/NetworkManager");
+ if (!cls)
+ {
+ goto failed;
+ }
+ this->cls = (*env)->NewGlobalRef(env, cls);
+ method_id = (*env)->GetMethodID(env, cls, "<init>",
+ "(Landroid/content/Context;)V");
+ if (!method_id)
+ {
+ goto failed;
+ }
+ obj = (*env)->NewObject(env, cls, method_id, context);
+ if (!obj)
+ {
+ goto failed;
+ }
+ this->obj = (*env)->NewGlobalRef(env, obj);
+ androidjni_detach_thread();
+ return &this->public;
+
+failed:
+ DBG1(DBG_KNL, "failed to build NetworkManager object");
+ androidjni_exception_occurred(env);
+ androidjni_detach_thread();
+ destroy(this);
+ return NULL;
+};
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
new file mode 100644
index 000000000..634816405
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup network_manager network_manager
+ * @{ @ingroup kernel_android
+ */
+
+#ifndef NETWORK_MANAGER_H_
+#define NETWORK_MANAGER_H_
+
+#include <jni.h>
+
+#include <library.h>
+#include <utils/host.h>
+
+typedef struct network_manager_t network_manager_t;
+
+/**
+ * Callback called if connectivity changes somehow.
+ *
+ * Implementation should be quick as the call is made by the Java apps main
+ * thread.
+ *
+ * @param data data supplied during registration
+ * @param disconnected TRUE if currently disconnected
+ */
+typedef void (*connectivity_cb_t)(void *data, bool disconnected);
+
+/**
+ * NetworkManager, used to listen for network changes and retrieve local IP
+ * addresses.
+ *
+ * Communicates with NetworkManager via JNI
+ */
+struct network_manager_t {
+
+ /**
+ * Get a local address
+ *
+ * @param ipv4 TRUE to get an IPv4 address
+ * @return the address or NULL if none available
+ */
+ host_t *(*get_local_address)(network_manager_t *this, bool ipv4);
+
+ /**
+ * Register a callback that is called if connectivity changes
+ *
+ * @note Only the first registered callback is currently used
+ *
+ * @param cb callback to register
+ * @param data data provided to callback
+ */
+ void (*add_connectivity_cb)(network_manager_t *this, connectivity_cb_t cb,
+ void *data);
+
+ /**
+ * Unregister a previously registered callback for connectivity changes
+ *
+ * @param cb previously registered callback
+ */
+ void (*remove_connectivity_cb)(network_manager_t *this,
+ connectivity_cb_t cb);
+
+ /**
+ * Destroy a network_manager_t instance
+ */
+ void (*destroy)(network_manager_t *this);
+};
+
+/**
+ * Create a network_manager_t instance
+ *
+ * @param context Context object
+ * @return network_manager_t instance
+ */
+network_manager_t *network_manager_create(jobject context);
+
+#endif /** NETWORK_MANAGER_H_ @}*/