2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 package org
.strongswan
.android
.logic
;
20 import org
.strongswan
.android
.data
.VpnProfile
;
21 import org
.strongswan
.android
.data
.VpnProfileDataSource
;
22 import org
.strongswan
.android
.logic
.VpnStateService
.ErrorState
;
23 import org
.strongswan
.android
.logic
.VpnStateService
.State
;
25 import android
.app
.Service
;
26 import android
.content
.ComponentName
;
27 import android
.content
.Intent
;
28 import android
.content
.ServiceConnection
;
29 import android
.net
.VpnService
;
30 import android
.os
.Bundle
;
31 import android
.os
.IBinder
;
32 import android
.util
.Log
;
34 public class CharonVpnService
extends VpnService
implements Runnable
36 private static final String TAG
= CharonVpnService
.class.getSimpleName();
37 private VpnProfileDataSource mDataSource
;
38 private Thread mConnectionHandler
;
39 private VpnProfile mCurrentProfile
;
40 private VpnProfile mNextProfile
;
41 private volatile boolean mProfileUpdated
;
42 private volatile boolean mTerminate
;
43 private VpnStateService mService
;
44 private final Object mServiceLock
= new Object();
45 private final ServiceConnection mServiceConnection
= new ServiceConnection() {
47 public void onServiceDisconnected(ComponentName name
)
48 { /* since the service is local this is theoretically only called when the process is terminated */
49 synchronized (mServiceLock
)
56 public void onServiceConnected(ComponentName name
, IBinder service
)
58 synchronized (mServiceLock
)
60 mService
= ((VpnStateService
.LocalBinder
)service
).getService();
62 /* we are now ready to start the handler thread */
63 mConnectionHandler
.start();
68 * as defined in charonservice.h
70 static final int STATE_CHILD_SA_UP
= 1;
71 static final int STATE_CHILD_SA_DOWN
= 2;
72 static final int STATE_AUTH_ERROR
= 3;
73 static final int STATE_PEER_AUTH_ERROR
= 4;
74 static final int STATE_LOOKUP_ERROR
= 5;
75 static final int STATE_UNREACHABLE_ERROR
= 6;
76 static final int STATE_GENERIC_ERROR
= 7;
79 public int onStartCommand(Intent intent
, int flags
, int startId
)
83 Bundle bundle
= intent
.getExtras();
84 VpnProfile profile
= null
;
87 profile
= mDataSource
.getVpnProfile(bundle
.getLong(VpnProfileDataSource
.KEY_ID
));
90 String password
= bundle
.getString(VpnProfileDataSource
.KEY_PASSWORD
);
91 profile
.setPassword(password
);
94 setNextProfile(profile
);
96 return START_NOT_STICKY
;
100 public void onCreate()
102 mDataSource
= new VpnProfileDataSource(this);
104 /* use a separate thread as main thread for charon */
105 mConnectionHandler
= new Thread(this);
106 /* the thread is started when the service is bound */
107 bindService(new Intent(this, VpnStateService
.class),
108 mServiceConnection
, Service
.BIND_AUTO_CREATE
);
112 public void onRevoke()
113 { /* the system revoked the rights grated with the initial prepare() call.
114 * called when the user clicks disconnect in the system's VPN dialog */
115 setNextProfile(null
);
119 public void onDestroy()
122 setNextProfile(null
);
125 mConnectionHandler
.join();
127 catch (InterruptedException e
)
131 if (mService
!= null
)
133 unbindService(mServiceConnection
);
139 * Set the profile that is to be initiated next. Notify the handler thread.
141 * @param profile the profile to initiate
143 private void setNextProfile(VpnProfile profile
)
147 this.mNextProfile
= profile
;
148 mProfileUpdated
= true
;
162 while (!mProfileUpdated
)
167 mProfileUpdated
= false
;
168 stopCurrentConnection();
169 if (mNextProfile
== null
)
172 setState(State
.DISABLED
);
180 mCurrentProfile
= mNextProfile
;
183 setProfile(mCurrentProfile
);
184 setError(ErrorState
.NO_ERROR
);
185 setState(State
.CONNECTING
);
188 Log
.i(TAG
, "charon started");
191 catch (InterruptedException ex
)
193 stopCurrentConnection();
194 setState(State
.DISABLED
);
201 * Stop any existing connection by deinitializing charon.
203 private void stopCurrentConnection()
207 if (mCurrentProfile
!= null
)
209 setState(State
.DISCONNECTING
);
210 deinitializeCharon();
211 Log
.i(TAG
, "charon stopped");
212 mCurrentProfile
= null
;
218 * Update the VPN profile on the state service. Called by the handler thread.
220 * @param profile currently active VPN profile
222 private void setProfile(VpnProfile profile
)
224 synchronized (mServiceLock
)
226 if (mService
!= null
)
228 mService
.setProfile(profile
);
234 * Update the current VPN state on the state service. Called by the handler
235 * thread and any of charon's threads.
237 * @param state current state
239 private void setState(State state
)
241 synchronized (mServiceLock
)
243 if (mService
!= null
)
245 mService
.setState(state
);
251 * Set an error on the state service. Called by the handler thread and any
252 * of charon's threads.
254 * @param error error state
256 private void setError(ErrorState error
)
258 synchronized (mServiceLock
)
260 if (mService
!= null
)
262 mService
.setError(error
);
268 * Set an error on the state service and disconnect the current connection.
269 * This is not done by calling stopCurrentConnection() above, but instead
270 * is done asynchronously via state service.
272 * @param error error state
274 private void setErrorDisconnect(ErrorState error
)
276 synchronized (mServiceLock
)
278 if (mService
!= null
)
280 mService
.setError(error
);
281 mService
.disconnect();
287 * Updates the state of the current connection.
288 * Called via JNI by different threads (but not concurrently).
290 * @param status new state
292 public void updateStatus(int status
)
296 case STATE_CHILD_SA_DOWN
:
297 synchronized (mServiceLock
)
299 /* since this state is also reached when the SA is closed remotely,
300 * we call disconnect() to make sure charon is properly deinitialized */
301 if (mService
!= null
)
303 mService
.disconnect();
307 case STATE_CHILD_SA_UP
:
308 setState(State
.CONNECTED
);
310 case STATE_AUTH_ERROR
:
311 setErrorDisconnect(ErrorState
.AUTH_FAILED
);
313 case STATE_PEER_AUTH_ERROR
:
314 setErrorDisconnect(ErrorState
.PEER_AUTH_FAILED
);
316 case STATE_LOOKUP_ERROR
:
317 setErrorDisconnect(ErrorState
.LOOKUP_FAILED
);
319 case STATE_UNREACHABLE_ERROR
:
320 setErrorDisconnect(ErrorState
.UNREACHABLE
);
322 case STATE_GENERIC_ERROR
:
323 setErrorDisconnect(ErrorState
.GENERIC_ERROR
);
326 Log
.e(TAG
, "Unknown status code received");
332 * Initialization of charon, provided by libandroidbridge.so
334 public native void initializeCharon();
337 * Deinitialize charon, provided by libandroidbridge.so
339 public native void deinitializeCharon();
342 * The libraries are extracted to /data/data/org.strongswan.android/...
343 * during installation.
347 System
.loadLibrary("crypto");
348 System
.loadLibrary("strongswan");
349 System
.loadLibrary("hydra");
350 System
.loadLibrary("charon");
351 System
.loadLibrary("ipsec");
352 System
.loadLibrary("androidbridge");