android: Add state of IMC to VpnStateService and update it via JNI
authorTobias Brunner <tobias@strongswan.org>
Wed, 15 May 2013 13:52:16 +0000 (15:52 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 8 Jul 2013 16:49:28 +0000 (18:49 +0200)
src/frontends/android/jni/libandroidbridge/byod/imc_android.c
src/frontends/android/jni/libandroidbridge/charonservice.c
src/frontends/android/jni/libandroidbridge/charonservice.h
src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java
src/frontends/android/src/org/strongswan/android/logic/imc/ImcState.java [new file with mode: 0644]

index 7dfc3a2..7067e1f 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "imc_android_state.h"
 #include "../android_jni.h"
+#include "../charonservice.h"
 
 #include <tnc/tnc.h>
 #include <libpts.h>
@@ -99,6 +100,29 @@ static TNC_Result tnc_imc_initialize(TNC_IMCID imc_id,
 }
 
 /**
+ * Update the state in the GUI.
+ */
+static void update_imc_state(TNC_ConnectionState state)
+{
+       android_imc_state_t imc_state = ANDROID_IMC_STATE_UNKNOWN;
+
+       switch (state)
+       {       /* map connection states to the values used by the GUI */
+               case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
+                       imc_state = ANDROID_IMC_STATE_ALLOW;
+                       break;
+               case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
+                       imc_state = ANDROID_IMC_STATE_ISOLATE;
+                       break;
+               case TNC_CONNECTION_STATE_ACCESS_NONE:
+                       imc_state = ANDROID_IMC_STATE_BLOCK;
+                       break;
+       }
+
+       charonservice->update_imc_state(charonservice, imc_state);
+}
+
+/**
  * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3
  */
 static TNC_Result tnc_imc_notifyconnectionchange(TNC_IMCID imc_id,
@@ -128,6 +152,11 @@ static TNC_Result tnc_imc_notifyconnectionchange(TNC_IMCID imc_id,
                        return TNC_RESULT_SUCCESS;
                case TNC_CONNECTION_STATE_DELETE:
                        return imc_android->delete_state(imc_android, connection_id);
+               case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
+               case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
+               case TNC_CONNECTION_STATE_ACCESS_NONE:
+                       update_imc_state(new_state);
+                       /* fall-through */
                default:
                        return imc_android->change_state(imc_android, connection_id,
                                                                                         new_state, NULL);
index 1c62e28..115b532 100644 (file)
@@ -162,6 +162,30 @@ failed:
        return success;
 }
 
+METHOD(charonservice_t, update_imc_state, bool,
+       private_charonservice_t *this, android_imc_state_t state)
+{
+       JNIEnv *env;
+       jmethodID method_id;
+       bool success = FALSE;
+
+       androidjni_attach_thread(&env);
+
+       method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
+                                                                       "updateImcState", "(I)V");
+       if (!method_id)
+       {
+               goto failed;
+       }
+       (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)state);
+       success = !androidjni_exception_occurred(env);
+
+failed:
+       androidjni_exception_occurred(env);
+       androidjni_detach_thread();
+       return success;
+}
+
 /**
  * Bypass a single socket
  */
@@ -466,6 +490,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
        INIT(this,
                .public = {
                        .update_status = _update_status,
+                       .update_imc_state = _update_imc_state,
                        .bypass_socket = _bypass_socket,
                        .get_trusted_certificates = _get_trusted_certificates,
                        .get_user_certificate = _get_user_certificate,
index f142f78..90f5bc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
  * Copyright (C) 2012 Giuliano Grassi
  * Copyright (C) 2012 Ralf Sager
  * Hochschule fuer Technik Rapperswil
@@ -41,6 +41,7 @@
 #include <collections/linked_list.h>
 
 typedef enum android_vpn_state_t android_vpn_state_t;
+typedef enum android_imc_state_t android_imc_state_t;
 typedef struct charonservice_t charonservice_t;
 
 /**
@@ -57,6 +58,16 @@ enum android_vpn_state_t {
 };
 
 /**
+ * Final IMC state as defined in ImcState.java
+ */
+enum android_imc_state_t {
+       ANDROID_IMC_STATE_UNKNOWN = 0,
+       ANDROID_IMC_STATE_ALLOW = 1,
+       ANDROID_IMC_STATE_BLOCK = 2,
+       ANDROID_IMC_STATE_ISOLATE = 3,
+};
+
+/**
  * Public interface of charonservice.
  *
  * Used to communicate with CharonVpnService via JNI
@@ -72,6 +83,14 @@ struct charonservice_t {
        bool (*update_status)(charonservice_t *this, android_vpn_state_t code);
 
        /**
+        * Update final IMC state in the Java domain (UI)
+        *
+        * @param state                 IMC state
+        * @return                              TRUE on success
+        */
+       bool (*update_imc_state)(charonservice_t *this, android_imc_state_t state);
+
+       /**
         * Install a bypass policy for the given socket using the protect() Method
         * of the Android VpnService interface.
         *
index f08a481..9f920a9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
  * Copyright (C) 2012 Giuliano Grassi
  * Copyright (C) 2012 Ralf Sager
  * Hochschule fuer Technik Rapperswil
@@ -27,6 +27,7 @@ import org.strongswan.android.data.VpnProfile;
 import org.strongswan.android.data.VpnProfileDataSource;
 import org.strongswan.android.logic.VpnStateService.ErrorState;
 import org.strongswan.android.logic.VpnStateService.State;
+import org.strongswan.android.logic.imc.ImcState;
 import org.strongswan.android.ui.MainActivity;
 
 import android.app.PendingIntent;
@@ -208,6 +209,7 @@ public class CharonVpnService extends VpnService implements Runnable
                                                setProfile(mCurrentProfile);
                                                setError(ErrorState.NO_ERROR);
                                                setState(State.CONNECTING);
+                                               setImcState(ImcState.UNKNOWN);
                                                mIsDisconnecting = false;
 
                                                BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName());
@@ -297,6 +299,23 @@ public class CharonVpnService extends VpnService implements Runnable
        }
 
        /**
+        * Set the IMC state on the state service. Called by the handler thread and
+        * any of charon's threads.
+        *
+        * @param state IMC state
+        */
+       private void setImcState(ImcState state)
+       {
+               synchronized (mServiceLock)
+               {
+                       if (mService != null)
+                       {
+                               mService.setImcState(state);
+                       }
+               }
+       }
+
+       /**
         * Set an error on the state service and disconnect the current connection.
         * This is not done by calling stopCurrentConnection() above, but instead
         * is done asynchronously via state service.
@@ -356,6 +375,21 @@ public class CharonVpnService extends VpnService implements Runnable
        }
 
        /**
+        * Updates the IMC state of the current connection.
+        * Called via JNI by different threads (but not concurrently).
+        *
+        * @param value new state
+        */
+       public void updateImcState(int value)
+       {
+               ImcState state = ImcState.fromValue(value);
+               if (state != null)
+               {
+                       setImcState(state);
+               }
+       }
+
+       /**
         * Function called via JNI to generate a list of DER encoded CA certificates
         * as byte array.
         *
index 1c14cb6..62ca31f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
  * Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.concurrent.Callable;
 
 import org.strongswan.android.data.VpnProfile;
+import org.strongswan.android.logic.imc.ImcState;
 
 import android.app.Service;
 import android.content.Context;
@@ -36,6 +37,7 @@ public class VpnStateService extends Service
        private VpnProfile mProfile;
        private State mState = State.DISABLED;
        private ErrorState mError = ErrorState.NO_ERROR;
+       private ImcState mImcState = ImcState.UNKNOWN;
 
        public enum State
        {
@@ -147,6 +149,16 @@ public class VpnStateService extends Service
        }
 
        /**
+        * Get the current IMC state, if any.
+        *
+        * @return imc state
+        */
+       public ImcState getImcState()
+       {       /* only updated from the main thread so no synchronization needed */
+               return mImcState;
+       }
+
+       /**
         * Disconnect any existing connection and shutdown the daemon, the
         * VpnService is not stopped but it is reset so new connections can be
         * started.
@@ -261,4 +273,27 @@ public class VpnStateService extends Service
                        }
                });
        }
+
+       /**
+        * Set the current IMC state and notify all listeners, if changed.
+        *
+        * May be called from threads other than the main thread.
+        *
+        * @param error error state
+        */
+       public void setImcState(final ImcState state)
+       {
+               notifyListeners(new Callable<Boolean>() {
+                       @Override
+                       public Boolean call() throws Exception
+                       {
+                               if (VpnStateService.this.mImcState != state)
+                               {
+                                       VpnStateService.this.mImcState = state;
+                                       return true;
+                               }
+                               return false;
+                       }
+               });
+       }
 }
diff --git a/src/frontends/android/src/org/strongswan/android/logic/imc/ImcState.java b/src/frontends/android/src/org/strongswan/android/logic/imc/ImcState.java
new file mode 100644 (file)
index 0000000..4fc3834
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+package org.strongswan.android.logic.imc;
+
+public enum ImcState
+{
+       UNKNOWN(0),
+       ALLOW(1),
+       BLOCK(2),
+       ISOLATE(3);
+
+       private final int mValue;
+
+       private ImcState(int value)
+       {
+               mValue = value;
+       }
+
+       /**
+        * Get the numeric value of the IMC state.
+        * @return numeric value
+        */
+       public int getValue()
+       {
+               return mValue;
+       }
+
+       /**
+        * Get the enum entry from a numeric value, if defined
+        *
+        * @param value numeric value
+        * @return the enum entry or null
+        */
+       public static ImcState fromValue(int value)
+       {
+               for (ImcState state : ImcState.values())
+               {
+                       if (state.mValue == value)
+                       {
+                               return state;
+                       }
+               }
+               return null;
+       }
+}