2 * Copyright (C) 2012-2017 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
16 package org
.strongswan
.android
.logic
;
18 import android
.app
.Service
;
19 import android
.content
.Context
;
20 import android
.content
.Intent
;
21 import android
.os
.Binder
;
22 import android
.os
.Handler
;
23 import android
.os
.IBinder
;
25 import org
.strongswan
.android
.R
;
26 import org
.strongswan
.android
.data
.VpnProfile
;
27 import org
.strongswan
.android
.logic
.imc
.ImcState
;
28 import org
.strongswan
.android
.logic
.imc
.RemediationInstruction
;
30 import java
.util
.Collections
;
31 import java
.util
.HashSet
;
32 import java
.util
.LinkedList
;
33 import java
.util
.List
;
34 import java
.util
.concurrent
.Callable
;
36 public class VpnStateService
extends Service
38 private final HashSet
<VpnStateListener
> mListeners
= new HashSet
<VpnStateListener
>();
39 private final IBinder mBinder
= new LocalBinder();
40 private long mConnectionID
= 0;
41 private Handler mHandler
;
42 private VpnProfile mProfile
;
43 private State mState
= State
.DISABLED
;
44 private ErrorState mError
= ErrorState
.NO_ERROR
;
45 private ImcState mImcState
= ImcState
.UNKNOWN
;
46 private final LinkedList
<RemediationInstruction
> mRemediationInstructions
= new LinkedList
<RemediationInstruction
>();
56 public enum ErrorState
67 * Listener interface for bound clients that are interested in changes to
70 public interface VpnStateListener
72 public void stateChanged();
76 * Simple Binder that allows to directly access this Service class itself
77 * after binding to it.
79 public class LocalBinder
extends Binder
81 public VpnStateService
getService()
83 return VpnStateService
.this;
88 public void onCreate()
90 /* this handler allows us to notify listeners from the UI thread and
91 * not from the threads that actually report any state changes */
92 mHandler
= new Handler();
96 public IBinder
onBind(Intent intent
)
102 public void onDestroy()
107 * Register a listener with this Service. We assume this is called from
108 * the main thread so no synchronization is happening.
110 * @param listener listener to register
112 public void registerListener(VpnStateListener listener
)
114 mListeners
.add(listener
);
118 * Unregister a listener from this Service.
120 * @param listener listener to unregister
122 public void unregisterListener(VpnStateListener listener
)
124 mListeners
.remove(listener
);
128 * Get the current VPN profile.
132 public VpnProfile
getProfile()
133 { /* only updated from the main thread so no synchronization needed */
138 * Get the current connection ID. May be used to track which state
139 * changes have already been handled.
141 * Is increased when startConnection() is called.
143 * @return connection ID
145 public long getConnectionID()
146 { /* only updated from the main thread so no synchronization needed */
147 return mConnectionID
;
151 * Get the current state.
155 public State
getState()
156 { /* only updated from the main thread so no synchronization needed */
161 * Get the current error, if any.
165 public ErrorState
getErrorState()
166 { /* only updated from the main thread so no synchronization needed */
171 * Get a description of the current error, if any.
173 * @return error description text id
175 public int getErrorText()
180 if (mImcState
== ImcState
.BLOCK
)
182 return R
.string
.error_assessment_failed
;
186 return R
.string
.error_auth_failed
;
188 case PEER_AUTH_FAILED
:
189 return R
.string
.error_peer_auth_failed
;
191 return R
.string
.error_lookup_failed
;
193 return R
.string
.error_unreachable
;
195 return R
.string
.error_generic
;
200 * Get the current IMC state, if any.
204 public ImcState
getImcState()
205 { /* only updated from the main thread so no synchronization needed */
210 * Get the remediation instructions, if any.
212 * @return read-only list of instructions
214 public List
<RemediationInstruction
> getRemediationInstructions()
215 { /* only updated from the main thread so no synchronization needed */
216 return Collections
.unmodifiableList(mRemediationInstructions
);
220 * Disconnect any existing connection and shutdown the daemon, the
221 * VpnService is not stopped but it is reset so new connections can be
224 public void disconnect()
226 /* as soon as the TUN device is created by calling establish() on the
227 * VpnService.Builder object the system binds to the service and keeps
228 * bound until the file descriptor of the TUN device is closed. thus
229 * calling stopService() here would not stop (destroy) the service yet,
230 * instead we call startService() with a specific action which shuts down
231 * the daemon (and closes the TUN device, if any) */
232 Context context
= getApplicationContext();
233 Intent intent
= new Intent(context
, CharonVpnService
.class);
234 intent
.setAction(CharonVpnService
.DISCONNECT_ACTION
);
235 context
.startService(intent
);
239 * Update state and notify all listeners about the change. By using a Handler
240 * this is done from the main UI thread and not the initial reporter thread.
241 * Also, in doing the actual state change from the main thread, listeners
242 * see all changes and none are skipped.
244 * @param change the state update to perform before notifying listeners, returns true if state changed
246 private void notifyListeners(final Callable
<Boolean
> change
)
248 mHandler
.post(new Runnable() {
255 { /* otherwise there is no need to notify the listeners */
256 for (VpnStateListener listener
: mListeners
)
258 listener
.stateChanged();
271 * Called when a connection is started. Sets the currently active VPN
272 * profile, resets IMC and Error state variables, sets the State to
273 * CONNECTING, increases the connection ID, and notifies all listeners.
275 * May be called from threads other than the main thread.
277 * @param profile current profile
279 public void startConnection(final VpnProfile profile
)
281 notifyListeners(new Callable
<Boolean
>() {
283 public Boolean
call() throws Exception
285 VpnStateService
.this.mConnectionID
++;
286 VpnStateService
.this.mProfile
= profile
;
287 VpnStateService
.this.mState
= State
.CONNECTING
;
288 VpnStateService
.this.mError
= ErrorState
.NO_ERROR
;
289 VpnStateService
.this.mImcState
= ImcState
.UNKNOWN
;
290 VpnStateService
.this.mRemediationInstructions
.clear();
297 * Update the state and notify all listeners, if changed.
299 * May be called from threads other than the main thread.
301 * @param state new state
303 public void setState(final State state
)
305 notifyListeners(new Callable
<Boolean
>() {
307 public Boolean
call() throws Exception
309 if (VpnStateService
.this.mState
!= state
)
311 VpnStateService
.this.mState
= state
;
320 * Set the current error state and notify all listeners, if changed.
322 * May be called from threads other than the main thread.
324 * @param error error state
326 public void setError(final ErrorState error
)
328 notifyListeners(new Callable
<Boolean
>() {
330 public Boolean
call() throws Exception
332 if (VpnStateService
.this.mError
!= error
)
334 VpnStateService
.this.mError
= error
;
343 * Set the current IMC state and notify all listeners, if changed.
345 * Setting the state to UNKNOWN clears all remediation instructions.
347 * May be called from threads other than the main thread.
349 * @param state IMC state
351 public void setImcState(final ImcState state
)
353 notifyListeners(new Callable
<Boolean
>() {
355 public Boolean
call() throws Exception
357 if (state
== ImcState
.UNKNOWN
)
359 VpnStateService
.this.mRemediationInstructions
.clear();
361 if (VpnStateService
.this.mImcState
!= state
)
363 VpnStateService
.this.mImcState
= state
;
372 * Add the given remediation instruction to the internal list. Listeners
375 * Instructions are cleared if the IMC state is set to UNKNOWN.
377 * May be called from threads other than the main thread.
379 * @param instruction remediation instruction
381 public void addRemediationInstruction(final RemediationInstruction instruction
)
383 mHandler
.post(new Runnable() {
387 VpnStateService
.this.mRemediationInstructions
.add(instruction
);