]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
android: Register NetworkManager as BroadcastReceiver and relay events via JNI
authorTobias Brunner <tobias@strongswan.org>
Wed, 10 Oct 2012 12:14:30 +0000 (14:14 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 16 Oct 2012 12:16:17 +0000 (14:16 +0200)
src/frontends/android/AndroidManifest.xml
src/frontends/android/jni/libandroidbridge/charonservice.c
src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java

index f0ed02ff3a0607c09040d356414ad3ce14140a46..4655cd7fcae00b4a058d054e8866f7c55c2decd6 100644 (file)
@@ -23,6 +23,7 @@
     <uses-sdk android:minSdkVersion="14" />
 
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
     <application
         android:icon="@drawable/ic_launcher"
index 96a4da8f33b7b4121a91cbb818dc5acc467a608c..591ca90ecb1a20fdb65b1f620742b9171c1890d1 100644 (file)
@@ -414,7 +414,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
                .attr = android_attr_create(),
                .creds = android_creds_create(),
                .builder = vpnservice_builder_create(builder),
-               .network_manager = network_manager_create(),
+               .network_manager = network_manager_create(service),
                .vpn_service = (*env)->NewGlobalRef(env, service),
        );
        charonservice = &this->public;
index e7bcf1365fe1a7c09b51120e64f03d2202a1ee1b..9c97fbb14354cd522b1b00016aaead715106c298 100644 (file)
@@ -15,7 +15,9 @@
 #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;
 
@@ -35,6 +37,19 @@ struct private_network_manager_t {
         * 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*,
@@ -70,11 +85,99 @@ failed:
        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)
        {
@@ -85,13 +188,14 @@ METHOD(network_manager_t, destroy, void,
                (*env)->DeleteGlobalRef(env, this->cls);
        }
        androidjni_detach_thread();
+       this->mutex->destroy(this->mutex);
        free(this);
 }
 
 /*
  * Described in header.
  */
-network_manager_t *network_manager_create()
+network_manager_t *network_manager_create(jobject context)
 {
        private_network_manager_t *this;
        JNIEnv *env;
@@ -102,8 +206,11 @@ network_manager_t *network_manager_create()
        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);
@@ -114,12 +221,12 @@ network_manager_t *network_manager_create()
        }
        this->cls = (*env)->NewGlobalRef(env, cls);
        method_id = (*env)->GetMethodID(env, cls, "<init>",
-                                                                       "()V");
+                                                                       "(Landroid/content/Context;)V");
        if (!method_id)
        {
                goto failed;
        }
-       obj = (*env)->NewObject(env, cls, method_id);
+       obj = (*env)->NewObject(env, cls, method_id, context);
        if (!obj)
        {
                goto failed;
index cb0da7fa2ca6512a36bc3b0b1534cd5ffc98edd4..634816405e2dcfd35b8d291e37701c4c37850df6 100644 (file)
 typedef struct network_manager_t network_manager_t;
 
 /**
- * NetworkManager, used to retrieve local IP addresses.
+ * 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
  */
@@ -43,6 +55,25 @@ struct network_manager_t {
         */
        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
         */
@@ -52,8 +83,9 @@ struct network_manager_t {
 /**
  * Create a network_manager_t instance
  *
+ * @param context                              Context object
  * @return                                             network_manager_t instance
  */
-network_manager_t *network_manager_create();
+network_manager_t *network_manager_create(jobject context);
 
 #endif /** NETWORK_MANAGER_H_ @}*/
index d9ed49b34ec4325445141d0970496cc64277f525..160865fb70dc5648c2c46d77a1e38f4e9192af98 100644 (file)
@@ -22,12 +22,48 @@ import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.Enumeration;
 
-public class NetworkManager
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+public class NetworkManager extends BroadcastReceiver
 {
-       public NetworkManager()
+       private final Context mContext;
+       private boolean mRegistered;
+
+       public NetworkManager(Context context)
+       {
+               mContext = context;
+       }
+
+       public void Register()
+       {
+               mContext.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+       }
+
+       public void Unregister()
        {
+               mContext.unregisterReceiver(this);
        }
 
+       @Override
+       public void onReceive(Context context, Intent intent)
+       {
+               ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+               NetworkInfo info = cm.getActiveNetworkInfo();
+               networkChanged(info == null || !info.isConnected());
+       }
+
+       /**
+        * Notify the native parts about a network change
+        *
+        * @param disconnected true if no connection is available at the moment
+        */
+       public native void networkChanged(boolean disconnected);
+
        /**
         * Function that retrieves a local address of the given family.
         *