Implement kernel_ipsec_t.bypass_socket() via JNI and VpnService.protect()
[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.Intent;
26 import android.os.Binder;
27 import android.os.Handler;
28 import android.os.IBinder;
29
30 public class VpnStateService extends Service
31 {
32 private final List<VpnStateListener> mListeners = new ArrayList<VpnStateListener>();
33 private final IBinder mBinder = new LocalBinder();
34 private Handler mHandler;
35 private VpnProfile mProfile;
36 private State mState = State.DISABLED;
37 private ErrorState mError = ErrorState.NO_ERROR;
38
39 public enum State
40 {
41 DISABLED,
42 CONNECTING,
43 CONNECTED,
44 DISCONNECTING,
45 }
46
47 public enum ErrorState
48 {
49 NO_ERROR,
50 AUTH_FAILED,
51 PEER_AUTH_FAILED,
52 LOOKUP_FAILED,
53 UNREACHABLE,
54 GENERIC_ERROR,
55 }
56
57 /**
58 * Listener interface for bound clients that are interested in changes to
59 * this Service.
60 */
61 public interface VpnStateListener
62 {
63 public void stateChanged();
64 }
65
66 /**
67 * Simple Binder that allows to directly access this Service class itself
68 * after binding to it.
69 */
70 public class LocalBinder extends Binder
71 {
72 public VpnStateService getService()
73 {
74 return VpnStateService.this;
75 }
76 }
77
78 @Override
79 public void onCreate()
80 {
81 /* this handler allows us to notify listeners from the UI thread and
82 * not from the threads that actually report any state changes */
83 mHandler = new Handler();
84 }
85
86 @Override
87 public IBinder onBind(Intent intent)
88 {
89 return mBinder;
90 }
91
92 @Override
93 public void onDestroy()
94 {
95 }
96
97 /**
98 * Register a listener with this Service. We assume this is called from
99 * the main thread so no synchronization is happening.
100 *
101 * @param listener listener to register
102 */
103 public void registerListener(VpnStateListener listener)
104 {
105 mListeners.add(listener);
106 }
107
108 /**
109 * Unregister a listener from this Service.
110 *
111 * @param listener listener to unregister
112 */
113 public void unregisterListener(VpnStateListener listener)
114 {
115 mListeners.remove(listener);
116 }
117
118 /**
119 * Get the current VPN profile.
120 *
121 * @return profile
122 */
123 public VpnProfile getProfile()
124 { /* only updated from the main thread so no synchronization needed */
125 return mProfile;
126 }
127
128 /**
129 * Get the current state.
130 *
131 * @return state
132 */
133 public State getState()
134 { /* only updated from the main thread so no synchronization needed */
135 return mState;
136 }
137
138 /**
139 * Get the current error, if any.
140 *
141 * @return error
142 */
143 public ErrorState getErrorState()
144 { /* only updated from the main thread so no synchronization needed */
145 return mError;
146 }
147
148 /**
149 * Update state and notify all listeners about the change. By using a Handler
150 * this is done from the main UI thread and not the initial reporter thread.
151 * Also, in doing the actual state change from the main thread, listeners
152 * see all changes and none are skipped.
153 *
154 * @param change the state update to perform before notifying listeners, returns true if state changed
155 */
156 private void notifyListeners(final Callable<Boolean> change)
157 {
158 mHandler.post(new Runnable() {
159 @Override
160 public void run()
161 {
162 try
163 {
164 if (change.call())
165 { /* otherwise there is no need to notify the listeners */
166 for (VpnStateListener listener : mListeners)
167 {
168 listener.stateChanged();
169 }
170 }
171 }
172 catch (Exception e)
173 {
174 e.printStackTrace();
175 }
176 }
177 });
178 }
179
180 /**
181 * Set the VPN profile currently active. Listeners are not notified.
182 *
183 * May be called from threads other than the main thread.
184 *
185 * @param profile current profile
186 */
187 public void setProfile(final VpnProfile profile)
188 {
189 /* even though we don't notify the listeners the update is done from the
190 * same handler so updates are predictable for listeners */
191 mHandler.post(new Runnable() {
192 @Override
193 public void run()
194 {
195 VpnStateService.this.mProfile = profile;
196 }
197 });
198 }
199
200 /**
201 * Update the state and notify all listeners, if changed.
202 *
203 * May be called from threads other than the main thread.
204 *
205 * @param state new state
206 */
207 public void setState(final State state)
208 {
209 notifyListeners(new Callable<Boolean>() {
210 @Override
211 public Boolean call() throws Exception
212 {
213 if (VpnStateService.this.mState != state)
214 {
215 VpnStateService.this.mState = state;
216 return true;
217 }
218 return false;
219 }
220 });
221 }
222
223 /**
224 * Set the current error state and notify all listeners, if changed.
225 *
226 * May be called from threads other than the main thread.
227 *
228 * @param error error state
229 */
230 public void setError(final ErrorState error)
231 {
232 notifyListeners(new Callable<Boolean>() {
233 @Override
234 public Boolean call() throws Exception
235 {
236 if (VpnStateService.this.mError != error)
237 {
238 VpnStateService.this.mError = error;
239 return true;
240 }
241 return false;
242 }
243 });
244 }
245 }