CharonVpnService reacts on Intents and properly inits/deinits charon
authorTobias Brunner <tobias@strongswan.org>
Wed, 8 Aug 2012 09:54:36 +0000 (11:54 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 13 Aug 2012 09:00:27 +0000 (11:00 +0200)
Charon is initialized with every new connection attempt and
deinitialized when the service is terminated or it receives an empty
Intent (or before starting a new connection).

A separate thread is used to handle the connection attempts, this thread
acts as main thread for charon.

src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java

index b32f9ae..2f11cf1 100644 (file)
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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;
 
 package org.strongswan.android.logic;
 
+import org.strongswan.android.data.VpnProfile;
+import org.strongswan.android.data.VpnProfileDataSource;
+
 import android.content.Intent;
 import android.net.VpnService;
 import android.content.Intent;
 import android.net.VpnService;
+import android.os.Bundle;
+import android.util.Log;
 
 
-public class CharonVpnService extends VpnService
+public class CharonVpnService extends VpnService implements Runnable
 {
 {
+       private static final String TAG = CharonVpnService.class.getSimpleName();
+       private VpnProfileDataSource mDataSource;
+       private Thread mConnectionHandler;
+       private VpnProfile mCurrentProfile;
+       private VpnProfile mNextProfile;
+       private volatile boolean mProfileUpdated;
+       private volatile boolean mTerminate;
 
        @Override
        public int onStartCommand(Intent intent, int flags, int startId)
        {
 
        @Override
        public int onStartCommand(Intent intent, int flags, int startId)
        {
-               // called whenever the service is started with startService
-               // create our own thread because we are running in the calling processes
-               // main thread
-               return super.onStartCommand(intent, flags, startId);
+               if (intent != null)
+               {
+                       Bundle bundle = intent.getExtras();
+                       VpnProfile profile = null;
+                       if (bundle != null)
+                       {
+                               profile = mDataSource.getVpnProfile(bundle.getLong(VpnProfileDataSource.KEY_ID));
+                               if (profile != null)
+                               {
+                                       String password = bundle.getString(VpnProfileDataSource.KEY_PASSWORD);
+                                       profile.setPassword(password);
+                               }
+                       }
+                       setNextProfile(profile);
+               }
+               return START_NOT_STICKY;
        }
 
        @Override
        public void onCreate()
        {
        }
 
        @Override
        public void onCreate()
        {
-               // onCreate is only called once
-               initializeCharon();
-               super.onCreate();
+               mDataSource = new VpnProfileDataSource(this);
+               mDataSource.open();
+               /* use a separate thread as main thread for charon */
+               mConnectionHandler = new Thread(this);
+               mConnectionHandler.start();
+       }
+
+       @Override
+       public void onRevoke()
+       {       /* the system revoked the rights grated with the initial prepare() call.
+                * called when the user clicks disconnect in the system's VPN dialog */
+               setNextProfile(null);
        }
 
        @Override
        public void onDestroy()
        {
        }
 
        @Override
        public void onDestroy()
        {
-               // called once the service is to be destroyed
-               deinitializeCharon();
-               super.onDestroy();
+               mTerminate = true;
+               setNextProfile(null);
+               try
+               {
+                       mConnectionHandler.join();
+               }
+               catch (InterruptedException e)
+               {
+                       e.printStackTrace();
+               }
+               mDataSource.close();
+       }
+
+       /**
+        * Set the profile that is to be initiated next. Notify the handler thread.
+        *
+        * @param profile the profile to initiate
+        */
+       private void setNextProfile(VpnProfile profile)
+       {
+               synchronized (this)
+               {
+                       this.mNextProfile = profile;
+                       mProfileUpdated = true;
+                       notifyAll();
+               }
+       }
+
+       @Override
+       public void run()
+       {
+               while (true)
+               {
+                       synchronized (this)
+                       {
+                               try
+                               {
+                                       while (!mProfileUpdated)
+                                       {
+                                               wait();
+                                       }
+
+                                       mProfileUpdated = false;
+                                       stopCurrentConnection();
+                                       if (mNextProfile == null)
+                                       {
+                                               if (mTerminate)
+                                               {
+                                                       break;
+                                               }
+                                       }
+                                       else
+                                       {
+                                               mCurrentProfile = mNextProfile;
+                                               mNextProfile = null;
+
+                                               initializeCharon();
+                                               Log.i(TAG, "charon started");
+                                       }
+                               }
+                               catch (InterruptedException ex)
+                               {
+                                       stopCurrentConnection();
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Stop any existing connection by deinitializing charon.
+        */
+       private void stopCurrentConnection()
+       {
+               synchronized (this)
+               {
+                       if (mCurrentProfile != null)
+                       {
+                               deinitializeCharon();
+                               Log.i(TAG, "charon stopped");
+                               mCurrentProfile = null;
+                       }
+               }
        }
 
        /**
        }
 
        /**