2 * Copyright (C) 2012 Tobias Brunner
3 * 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 java
.util
.ArrayList
;
19 import java
.util
.List
;
20 import java
.util
.concurrent
.Callable
;
22 import org
.strongswan
.android
.data
.VpnProfile
;
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
;
31 public class VpnStateService
extends Service
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
;
48 public enum ErrorState
59 * Listener interface for bound clients that are interested in changes to
62 public interface VpnStateListener
64 public void stateChanged();
68 * Simple Binder that allows to directly access this Service class itself
69 * after binding to it.
71 public class LocalBinder
extends Binder
73 public VpnStateService
getService()
75 return VpnStateService
.this;
80 public void onCreate()
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();
88 public IBinder
onBind(Intent intent
)
94 public void onDestroy()
99 * Register a listener with this Service. We assume this is called from
100 * the main thread so no synchronization is happening.
102 * @param listener listener to register
104 public void registerListener(VpnStateListener listener
)
106 mListeners
.add(listener
);
110 * Unregister a listener from this Service.
112 * @param listener listener to unregister
114 public void unregisterListener(VpnStateListener listener
)
116 mListeners
.remove(listener
);
120 * Get the current VPN profile.
124 public VpnProfile
getProfile()
125 { /* only updated from the main thread so no synchronization needed */
130 * Get the current state.
134 public State
getState()
135 { /* only updated from the main thread so no synchronization needed */
140 * Get the current error, if any.
144 public ErrorState
getErrorState()
145 { /* only updated from the main thread so no synchronization needed */
150 * Disconnect any existing connection and shutdown the daemon, the
151 * VpnService is not stopped but it is reset so new connections can be
154 public void disconnect()
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
);
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.
173 * @param change the state update to perform before notifying listeners, returns true if state changed
175 private void notifyListeners(final Callable
<Boolean
> change
)
177 mHandler
.post(new Runnable() {
184 { /* otherwise there is no need to notify the listeners */
185 for (VpnStateListener listener
: mListeners
)
187 listener
.stateChanged();
200 * Set the VPN profile currently active. Listeners are not notified.
202 * May be called from threads other than the main thread.
204 * @param profile current profile
206 public void setProfile(final VpnProfile profile
)
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() {
214 VpnStateService
.this.mProfile
= profile
;
220 * Update the state and notify all listeners, if changed.
222 * May be called from threads other than the main thread.
224 * @param state new state
226 public void setState(final State state
)
228 notifyListeners(new Callable
<Boolean
>() {
230 public Boolean
call() throws Exception
232 if (VpnStateService
.this.mState
!= state
)
234 VpnStateService
.this.mState
= state
;
243 * Set the current error state and notify all listeners, if changed.
245 * May be called from threads other than the main thread.
247 * @param error error state
249 public void setError(final ErrorState error
)
251 notifyListeners(new Callable
<Boolean
>() {
253 public Boolean
call() throws Exception
255 if (VpnStateService
.this.mError
!= error
)
257 VpnStateService
.this.mError
= error
;