Add a function to disconnect any current VPN connection
[strongswan.git] / src / frontends / android / src / org / strongswan / android / logic / VpnStateService.java
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 package org.strongswan.android.logic;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.Callable;
21
22 import org.strongswan.android.data.VpnProfile;
23
24 import android.app.Service;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Binder;
28 import android.os.Handler;
29 import android.os.IBinder;
30
31 public class VpnStateService extends Service
32 {
33 private final List<VpnStateListener> mListeners = new ArrayList<VpnStateListener>();
34 private final IBinder mBinder = new LocalBinder();
35 private Handler mHandler;
36 private VpnProfile mProfile;
37 private State mState = State.DISABLED;
38 private ErrorState mError = ErrorState.NO_ERROR;
39
40 public enum State
41 {
42 DISABLED,
43 CONNECTING,
44 CONNECTED,
45 DISCONNECTING,
46 }
47
48 public enum ErrorState
49 {
50 NO_ERROR,
51 AUTH_FAILED,
52 PEER_AUTH_FAILED,
53 LOOKUP_FAILED,
54 UNREACHABLE,
55 GENERIC_ERROR,
56 }
57
58 /**
59 * Listener interface for bound clients that are interested in changes to
60 * this Service.
61 */
62 public interface VpnStateListener
63 {
64 public void stateChanged();
65 }
66
67 /**
68 * Simple Binder that allows to directly access this Service class itself
69 * after binding to it.
70 */
71 public class LocalBinder extends Binder
72 {
73 public VpnStateService getService()
74 {
75 return VpnStateService.this;
76 }
77 }
78
79 @Override
80 public void onCreate()
81 {
82 /* this handler allows us to notify listeners from the UI thread and
83 * not from the threads that actually report any state changes */
84 mHandler = new Handler();
85 }
86
87 @Override
88 public IBinder onBind(Intent intent)
89 {
90 return mBinder;
91 }
92
93 @Override
94 public void onDestroy()
95 {
96 }
97
98 /**
99 * Register a listener with this Service. We assume this is called from
100 * the main thread so no synchronization is happening.
101 *
102 * @param listener listener to register
103 */
104 public void registerListener(VpnStateListener listener)
105 {
106 mListeners.add(listener);
107 }
108
109 /**
110 * Unregister a listener from this Service.
111 *
112 * @param listener listener to unregister
113 */
114 public void unregisterListener(VpnStateListener listener)
115 {
116 mListeners.remove(listener);
117 }
118
119 /**
120 * Get the current VPN profile.
121 *
122 * @return profile
123 */
124 public VpnProfile getProfile()
125 { /* only updated from the main thread so no synchronization needed */
126 return mProfile;
127 }
128
129 /**
130 * Get the current state.
131 *
132 * @return state
133 */
134 public State getState()
135 { /* only updated from the main thread so no synchronization needed */
136 return mState;
137 }
138
139 /**
140 * Get the current error, if any.
141 *
142 * @return error
143 */
144 public ErrorState getErrorState()
145 { /* only updated from the main thread so no synchronization needed */
146 return mError;
147 }
148
149 /**
150 * Disconnect any existing connection and shutdown the daemon, the
151 * VpnService is not stopped but it is reset so new connections can be
152 * started.
153 */
154 public void disconnect()
155 {
156 /* as soon as the TUN device is created by calling establish() on the
157 * VpnService.Builder object the system binds to the service and keeps
158 * bound until the file descriptor of the TUN device is closed. thus
159 * calling stopService() here would not stop (destroy) the service yet,
160 * instead we call startService() with an empty Intent which shuts down
161 * the daemon (and closes the TUN device, if any) */
162 Context context = getApplicationContext();
163 Intent intent = new Intent(context, CharonVpnService.class);
164 context.startService(intent);
165 }
166
167 /**
168 * Update state and notify all listeners about the change. By using a Handler
169 * this is done from the main UI thread and not the initial reporter thread.
170 * Also, in doing the actual state change from the main thread, listeners
171 * see all changes and none are skipped.
172 *
173 * @param change the state update to perform before notifying listeners, returns true if state changed
174 */
175 private void notifyListeners(final Callable<Boolean> change)
176 {
177 mHandler.post(new Runnable() {
178 @Override
179 public void run()
180 {
181 try
182 {
183 if (change.call())
184 { /* otherwise there is no need to notify the listeners */
185 for (VpnStateListener listener : mListeners)
186 {
187 listener.stateChanged();
188 }
189 }
190 }
191 catch (Exception e)
192 {
193 e.printStackTrace();
194 }
195 }
196 });
197 }
198
199 /**
200 * Set the VPN profile currently active. Listeners are not notified.
201 *
202 * May be called from threads other than the main thread.
203 *
204 * @param profile current profile
205 */
206 public void setProfile(final VpnProfile profile)
207 {
208 /* even though we don't notify the listeners the update is done from the
209 * same handler so updates are predictable for listeners */
210 mHandler.post(new Runnable() {
211 @Override
212 public void run()
213 {
214 VpnStateService.this.mProfile = profile;
215 }
216 });
217 }
218
219 /**
220 * Update the state and notify all listeners, if changed.
221 *
222 * May be called from threads other than the main thread.
223 *
224 * @param state new state
225 */
226 public void setState(final State state)
227 {
228 notifyListeners(new Callable<Boolean>() {
229 @Override
230 public Boolean call() throws Exception
231 {
232 if (VpnStateService.this.mState != state)
233 {
234 VpnStateService.this.mState = state;
235 return true;
236 }
237 return false;
238 }
239 });
240 }
241
242 /**
243 * Set the current error state and notify all listeners, if changed.
244 *
245 * May be called from threads other than the main thread.
246 *
247 * @param error error state
248 */
249 public void setError(final ErrorState error)
250 {
251 notifyListeners(new Callable<Boolean>() {
252 @Override
253 public Boolean call() throws Exception
254 {
255 if (VpnStateService.this.mError != error)
256 {
257 VpnStateService.this.mError = error;
258 return true;
259 }
260 return false;
261 }
262 });
263 }
264 }