083b98c0c2b1bef8c787193babf681154f162aeb
[strongswan.git] / src / frontends / android / src / org / strongswan / android / logic / CharonVpnService.java
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
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>.
11 *
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
15 * for more details.
16 */
17
18 package org.strongswan.android.logic;
19
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;
24
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;
33
34 public class CharonVpnService extends VpnService implements Runnable
35 {
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() {
46 @Override
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)
50 {
51 mService = null;
52 }
53 }
54
55 @Override
56 public void onServiceConnected(ComponentName name, IBinder service)
57 {
58 synchronized (mServiceLock)
59 {
60 mService = ((VpnStateService.LocalBinder)service).getService();
61 }
62 /* we are now ready to start the handler thread */
63 mConnectionHandler.start();
64 }
65 };
66
67 @Override
68 public int onStartCommand(Intent intent, int flags, int startId)
69 {
70 if (intent != null)
71 {
72 Bundle bundle = intent.getExtras();
73 VpnProfile profile = null;
74 if (bundle != null)
75 {
76 profile = mDataSource.getVpnProfile(bundle.getLong(VpnProfileDataSource.KEY_ID));
77 if (profile != null)
78 {
79 String password = bundle.getString(VpnProfileDataSource.KEY_PASSWORD);
80 profile.setPassword(password);
81 }
82 }
83 setNextProfile(profile);
84 }
85 return START_NOT_STICKY;
86 }
87
88 @Override
89 public void onCreate()
90 {
91 mDataSource = new VpnProfileDataSource(this);
92 mDataSource.open();
93 /* use a separate thread as main thread for charon */
94 mConnectionHandler = new Thread(this);
95 /* the thread is started when the service is bound */
96 bindService(new Intent(this, VpnStateService.class),
97 mServiceConnection, Service.BIND_AUTO_CREATE);
98 }
99
100 @Override
101 public void onRevoke()
102 { /* the system revoked the rights grated with the initial prepare() call.
103 * called when the user clicks disconnect in the system's VPN dialog */
104 setNextProfile(null);
105 }
106
107 @Override
108 public void onDestroy()
109 {
110 mTerminate = true;
111 setNextProfile(null);
112 try
113 {
114 mConnectionHandler.join();
115 }
116 catch (InterruptedException e)
117 {
118 e.printStackTrace();
119 }
120 if (mService != null)
121 {
122 unbindService(mServiceConnection);
123 }
124 mDataSource.close();
125 }
126
127 /**
128 * Set the profile that is to be initiated next. Notify the handler thread.
129 *
130 * @param profile the profile to initiate
131 */
132 private void setNextProfile(VpnProfile profile)
133 {
134 synchronized (this)
135 {
136 this.mNextProfile = profile;
137 mProfileUpdated = true;
138 notifyAll();
139 }
140 }
141
142 @Override
143 public void run()
144 {
145 while (true)
146 {
147 synchronized (this)
148 {
149 try
150 {
151 while (!mProfileUpdated)
152 {
153 wait();
154 }
155
156 mProfileUpdated = false;
157 stopCurrentConnection();
158 if (mNextProfile == null)
159 {
160 setProfile(null);
161 setState(State.DISABLED);
162 if (mTerminate)
163 {
164 break;
165 }
166 }
167 else
168 {
169 mCurrentProfile = mNextProfile;
170 mNextProfile = null;
171
172 setProfile(mCurrentProfile);
173 setError(ErrorState.NO_ERROR);
174 setState(State.CONNECTING);
175
176 initializeCharon();
177 Log.i(TAG, "charon started");
178 }
179 }
180 catch (InterruptedException ex)
181 {
182 stopCurrentConnection();
183 setState(State.DISABLED);
184 }
185 }
186 }
187 }
188
189 /**
190 * Stop any existing connection by deinitializing charon.
191 */
192 private void stopCurrentConnection()
193 {
194 synchronized (this)
195 {
196 if (mCurrentProfile != null)
197 {
198 setState(State.DISCONNECTING);
199 deinitializeCharon();
200 Log.i(TAG, "charon stopped");
201 mCurrentProfile = null;
202 }
203 }
204 }
205
206 /**
207 * Update the VPN profile on the state service. Called by the handler thread.
208 *
209 * @param profile currently active VPN profile
210 */
211 private void setProfile(VpnProfile profile)
212 {
213 synchronized (mServiceLock)
214 {
215 if (mService != null)
216 {
217 mService.setProfile(profile);
218 }
219 }
220 }
221
222 /**
223 * Update the current VPN state on the state service. Called by the handler
224 * thread and any of charon's threads.
225 *
226 * @param state current state
227 */
228 private void setState(State state)
229 {
230 synchronized (mServiceLock)
231 {
232 if (mService != null)
233 {
234 mService.setState(state);
235 }
236 }
237 }
238
239 /**
240 * Set an error on the state service. Called by the handler thread and any
241 * of charon's threads.
242 *
243 * @param error error state
244 */
245 private void setError(ErrorState error)
246 {
247 synchronized (mServiceLock)
248 {
249 if (mService != null)
250 {
251 mService.setError(error);
252 }
253 }
254 }
255
256 /**
257 * Initialization of charon, provided by libandroidbridge.so
258 */
259 public native void initializeCharon();
260
261 /**
262 * Deinitialize charon, provided by libandroidbridge.so
263 */
264 public native void deinitializeCharon();
265
266 /*
267 * The libraries are extracted to /data/data/org.strongswan.android/...
268 * during installation.
269 */
270 static
271 {
272 System.loadLibrary("crypto");
273 System.loadLibrary("strongswan");
274 System.loadLibrary("hydra");
275 System.loadLibrary("charon");
276 System.loadLibrary("ipsec");
277 System.loadLibrary("androidbridge");
278 }
279 }