2 * Copyright (C) 2012-2017 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR Hochschule fuer Technik Rapperswil
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>.
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
18 package org
.strongswan
.android
.logic
;
20 import android
.annotation
.TargetApi
;
21 import android
.app
.Notification
;
22 import android
.app
.NotificationManager
;
23 import android
.app
.PendingIntent
;
24 import android
.app
.Service
;
25 import android
.content
.ComponentName
;
26 import android
.content
.Context
;
27 import android
.content
.Intent
;
28 import android
.content
.ServiceConnection
;
29 import android
.content
.pm
.PackageManager
;
30 import android
.net
.VpnService
;
31 import android
.os
.Build
;
32 import android
.os
.Bundle
;
33 import android
.os
.IBinder
;
34 import android
.os
.ParcelFileDescriptor
;
35 import android
.security
.KeyChain
;
36 import android
.security
.KeyChainException
;
37 import android
.support
.v4
.app
.NotificationCompat
;
38 import android
.support
.v4
.content
.ContextCompat
;
39 import android
.system
.OsConstants
;
40 import android
.util
.Log
;
42 import org
.strongswan
.android
.R
;
43 import org
.strongswan
.android
.data
.VpnProfile
;
44 import org
.strongswan
.android
.data
.VpnProfile
.SelectedAppsHandling
;
45 import org
.strongswan
.android
.data
.VpnProfileDataSource
;
46 import org
.strongswan
.android
.data
.VpnType
.VpnTypeFeature
;
47 import org
.strongswan
.android
.logic
.VpnStateService
.ErrorState
;
48 import org
.strongswan
.android
.logic
.VpnStateService
.State
;
49 import org
.strongswan
.android
.logic
.imc
.ImcState
;
50 import org
.strongswan
.android
.logic
.imc
.RemediationInstruction
;
51 import org
.strongswan
.android
.ui
.MainActivity
;
52 import org
.strongswan
.android
.utils
.IPRange
;
53 import org
.strongswan
.android
.utils
.IPRangeSet
;
54 import org
.strongswan
.android
.utils
.SettingsWriter
;
57 import java
.net
.Inet4Address
;
58 import java
.net
.Inet6Address
;
59 import java
.net
.InetAddress
;
60 import java
.net
.UnknownHostException
;
61 import java
.security
.PrivateKey
;
62 import java
.security
.cert
.CertificateEncodingException
;
63 import java
.security
.cert
.X509Certificate
;
64 import java
.util
.ArrayList
;
65 import java
.util
.List
;
66 import java
.util
.Locale
;
67 import java
.util
.SortedSet
;
69 public class CharonVpnService
extends VpnService
implements Runnable
, VpnStateService
.VpnStateListener
71 private static final String TAG
= CharonVpnService
.class.getSimpleName();
72 public static final String DISCONNECT_ACTION
= "org.strongswan.android.CharonVpnService.DISCONNECT";
73 public static final String LOG_FILE
= "charon.log";
74 public static final int VPN_STATE_NOTIFICATION_ID
= 1;
76 private String mLogFile
;
77 private VpnProfileDataSource mDataSource
;
78 private Thread mConnectionHandler
;
79 private VpnProfile mCurrentProfile
;
80 private volatile String mCurrentCertificateAlias
;
81 private volatile String mCurrentUserCertificateAlias
;
82 private VpnProfile mNextProfile
;
83 private volatile boolean mProfileUpdated
;
84 private volatile boolean mTerminate
;
85 private volatile boolean mIsDisconnecting
;
86 private volatile boolean mShowNotification
;
87 private VpnStateService mService
;
88 private final Object mServiceLock
= new Object();
89 private final ServiceConnection mServiceConnection
= new ServiceConnection() {
91 public void onServiceDisconnected(ComponentName name
)
92 { /* since the service is local this is theoretically only called when the process is terminated */
93 synchronized (mServiceLock
)
100 public void onServiceConnected(ComponentName name
, IBinder service
)
102 synchronized (mServiceLock
)
104 mService
= ((VpnStateService
.LocalBinder
)service
).getService();
106 /* we are now ready to start the handler thread */
107 mService
.registerListener(CharonVpnService
.this);
108 mConnectionHandler
.start();
113 * as defined in charonservice.h
115 static final int STATE_CHILD_SA_UP
= 1;
116 static final int STATE_CHILD_SA_DOWN
= 2;
117 static final int STATE_AUTH_ERROR
= 3;
118 static final int STATE_PEER_AUTH_ERROR
= 4;
119 static final int STATE_LOOKUP_ERROR
= 5;
120 static final int STATE_UNREACHABLE_ERROR
= 6;
121 static final int STATE_GENERIC_ERROR
= 7;
124 public int onStartCommand(Intent intent
, int flags
, int startId
)
128 if (DISCONNECT_ACTION
.equals(intent
.getAction()))
130 setNextProfile(null
);
134 Bundle bundle
= intent
.getExtras();
135 VpnProfile profile
= null
;
138 profile
= mDataSource
.getVpnProfile(bundle
.getLong(VpnProfileDataSource
.KEY_ID
));
141 String password
= bundle
.getString(VpnProfileDataSource
.KEY_PASSWORD
);
142 profile
.setPassword(password
);
145 setNextProfile(profile
);
148 return START_NOT_STICKY
;
152 public void onCreate()
154 mLogFile
= getFilesDir().getAbsolutePath() + File
.separator
+ LOG_FILE
;
156 mDataSource
= new VpnProfileDataSource(this);
158 /* use a separate thread as main thread for charon */
159 mConnectionHandler
= new Thread(this);
160 /* the thread is started when the service is bound */
161 bindService(new Intent(this, VpnStateService
.class),
162 mServiceConnection
, Service
.BIND_AUTO_CREATE
);
166 public void onRevoke()
167 { /* the system revoked the rights grated with the initial prepare() call.
168 * called when the user clicks disconnect in the system's VPN dialog */
169 setNextProfile(null
);
173 public void onDestroy()
176 setNextProfile(null
);
179 mConnectionHandler
.join();
181 catch (InterruptedException e
)
185 if (mService
!= null
)
187 mService
.unregisterListener(this);
188 unbindService(mServiceConnection
);
194 * Set the profile that is to be initiated next. Notify the handler thread.
196 * @param profile the profile to initiate
198 private void setNextProfile(VpnProfile profile
)
202 this.mNextProfile
= profile
;
203 mProfileUpdated
= true
;
217 while (!mProfileUpdated
)
222 mProfileUpdated
= false
;
223 stopCurrentConnection();
224 if (mNextProfile
== null
)
226 setState(State
.DISABLED
);
234 mCurrentProfile
= mNextProfile
;
237 /* store this in a separate (volatile) variable to avoid
238 * a possible deadlock during deinitialization */
239 mCurrentCertificateAlias
= mCurrentProfile
.getCertificateAlias();
240 mCurrentUserCertificateAlias
= mCurrentProfile
.getUserCertificateAlias();
242 startConnection(mCurrentProfile
);
243 mIsDisconnecting
= false
;
246 BuilderAdapter builder
= new BuilderAdapter(mCurrentProfile
);
247 if (initializeCharon(builder
, mLogFile
, mCurrentProfile
.getVpnType().has(VpnTypeFeature
.BYOD
)))
249 Log
.i(TAG
, "charon started");
250 SettingsWriter writer
= new SettingsWriter();
251 writer
.setValue("global.language", Locale
.getDefault().getLanguage());
252 writer
.setValue("global.mtu", mCurrentProfile
.getMTU());
253 writer
.setValue("connection.type", mCurrentProfile
.getVpnType().getIdentifier());
254 writer
.setValue("connection.server", mCurrentProfile
.getGateway());
255 writer
.setValue("connection.port", mCurrentProfile
.getPort());
256 writer
.setValue("connection.username", mCurrentProfile
.getUsername());
257 writer
.setValue("connection.password", mCurrentProfile
.getPassword());
258 writer
.setValue("connection.local_id", mCurrentProfile
.getLocalId());
259 writer
.setValue("connection.remote_id", mCurrentProfile
.getRemoteId());
260 initiate(writer
.serialize());
264 Log
.e(TAG
, "failed to start charon");
265 setError(ErrorState
.GENERIC_ERROR
);
266 setState(State
.DISABLED
);
267 mCurrentProfile
= null
;
271 catch (InterruptedException ex
)
273 stopCurrentConnection();
274 setState(State
.DISABLED
);
281 * Stop any existing connection by deinitializing charon.
283 private void stopCurrentConnection()
287 if (mCurrentProfile
!= null
)
289 setState(State
.DISCONNECTING
);
290 mIsDisconnecting
= true
;
291 deinitializeCharon();
292 Log
.i(TAG
, "charon stopped");
293 mCurrentProfile
= null
;
294 removeNotification();
300 * Add a permanent notification while we are connected to avoid the service getting killed by
301 * the system when low on memory.
303 private void addNotification()
305 mShowNotification
= true
;
306 startForeground(VPN_STATE_NOTIFICATION_ID
, buildNotification(false
));
310 * Remove the permanent notification.
312 private void removeNotification()
314 mShowNotification
= false
;
315 stopForeground(true
);
319 * Build a notification matching the current state
321 private Notification
buildNotification(boolean publicVersion
)
323 VpnProfile profile
= mService
.getProfile();
324 State state
= mService
.getState();
325 ErrorState error
= mService
.getErrorState();
330 name
= profile
.getName();
332 android
.support
.v4
.app
.NotificationCompat
.Builder builder
= new NotificationCompat
.Builder(this)
333 .setSmallIcon(R
.drawable
.ic_notification
)
334 .setCategory(NotificationCompat
.CATEGORY_SERVICE
)
335 .setVisibility(publicVersion ? NotificationCompat
.VISIBILITY_PUBLIC
336 : NotificationCompat
.VISIBILITY_PRIVATE
);
337 int s
= R
.string
.state_disabled
;
338 if (error
!= ErrorState
.NO_ERROR
)
340 s
= R
.string
.state_error
;
341 builder
.setSmallIcon(R
.drawable
.ic_notification_warning
);
342 builder
.setColor(ContextCompat
.getColor(this, R
.color
.error_text
));
349 s
= R
.string
.state_connecting
;
350 builder
.setSmallIcon(R
.drawable
.ic_notification_warning
);
351 builder
.setColor(ContextCompat
.getColor(this, R
.color
.warning_text
));
354 s
= R
.string
.state_connected
;
355 builder
.setColor(ContextCompat
.getColor(this, R
.color
.success_text
));
356 builder
.setUsesChronometer(true
);
359 s
= R
.string
.state_disconnecting
;
363 builder
.setContentTitle(getString(s
));
366 builder
.setContentText(name
);
367 builder
.setPublicVersion(buildNotification(true
));
370 Intent intent
= new Intent(getApplicationContext(), MainActivity
.class);
371 PendingIntent pending
= PendingIntent
.getActivity(getApplicationContext(), 0, intent
,
372 PendingIntent
.FLAG_UPDATE_CURRENT
);
373 builder
.setContentIntent(pending
);
374 return builder
.build();
378 public void stateChanged() {
379 if (mShowNotification
)
381 NotificationManager manager
= (NotificationManager
) getSystemService(Context
.NOTIFICATION_SERVICE
);
382 manager
.notify(VPN_STATE_NOTIFICATION_ID
, buildNotification(false
));
387 * Notify the state service about a new connection attempt.
388 * Called by the handler thread.
390 * @param profile currently active VPN profile
392 private void startConnection(VpnProfile profile
)
394 synchronized (mServiceLock
)
396 if (mService
!= null
)
398 mService
.startConnection(profile
);
404 * Update the current VPN state on the state service. Called by the handler
405 * thread and any of charon's threads.
407 * @param state current state
409 private void setState(State state
)
411 synchronized (mServiceLock
)
413 if (mService
!= null
)
415 mService
.setState(state
);
421 * Set an error on the state service. Called by the handler thread and any
422 * of charon's threads.
424 * @param error error state
426 private void setError(ErrorState error
)
428 synchronized (mServiceLock
)
430 if (mService
!= null
)
432 mService
.setError(error
);
438 * Set the IMC state on the state service. Called by the handler thread and
439 * any of charon's threads.
441 * @param state IMC state
443 private void setImcState(ImcState state
)
445 synchronized (mServiceLock
)
447 if (mService
!= null
)
449 mService
.setImcState(state
);
455 * Set an error on the state service. Called by the handler thread and any
456 * of charon's threads.
458 * @param error error state
460 private void setErrorDisconnect(ErrorState error
)
462 synchronized (mServiceLock
)
464 if (mService
!= null
)
466 if (!mIsDisconnecting
)
468 mService
.setError(error
);
475 * Updates the state of the current connection.
476 * Called via JNI by different threads (but not concurrently).
478 * @param status new state
480 public void updateStatus(int status
)
484 case STATE_CHILD_SA_DOWN
:
485 if (!mIsDisconnecting
)
487 setState(State
.CONNECTING
);
490 case STATE_CHILD_SA_UP
:
491 setState(State
.CONNECTED
);
493 case STATE_AUTH_ERROR
:
494 setErrorDisconnect(ErrorState
.AUTH_FAILED
);
496 case STATE_PEER_AUTH_ERROR
:
497 setErrorDisconnect(ErrorState
.PEER_AUTH_FAILED
);
499 case STATE_LOOKUP_ERROR
:
500 setErrorDisconnect(ErrorState
.LOOKUP_FAILED
);
502 case STATE_UNREACHABLE_ERROR
:
503 setErrorDisconnect(ErrorState
.UNREACHABLE
);
505 case STATE_GENERIC_ERROR
:
506 setErrorDisconnect(ErrorState
.GENERIC_ERROR
);
509 Log
.e(TAG
, "Unknown status code received");
515 * Updates the IMC state of the current connection.
516 * Called via JNI by different threads (but not concurrently).
518 * @param value new state
520 public void updateImcState(int value
)
522 ImcState state
= ImcState
.fromValue(value
);
530 * Add a remediation instruction to the VPN state service.
531 * Called via JNI by different threads (but not concurrently).
533 * @param xml XML text
535 public void addRemediationInstruction(String xml
)
537 for (RemediationInstruction instruction
: RemediationInstruction
.fromXml(xml
))
539 synchronized (mServiceLock
)
541 if (mService
!= null
)
543 mService
.addRemediationInstruction(instruction
);
550 * Function called via JNI to generate a list of DER encoded CA certificates
553 * @return a list of DER encoded CA certificates
555 private byte[][] getTrustedCertificates()
557 ArrayList
<byte[]> certs
= new ArrayList
<byte[]>();
558 TrustedCertificateManager certman
= TrustedCertificateManager
.getInstance().load();
561 String alias
= this.mCurrentCertificateAlias
;
564 X509Certificate cert
= certman
.getCACertificateFromAlias(alias
);
569 certs
.add(cert
.getEncoded());
573 for (X509Certificate cert
: certman
.getAllCACertificates().values())
575 certs
.add(cert
.getEncoded());
579 catch (CertificateEncodingException e
)
584 return certs
.toArray(new byte[certs
.size()][]);
588 * Function called via JNI to get a list containing the DER encoded certificates
589 * of the user selected certificate chain (beginning with the user certificate).
591 * Since this method is called from a thread of charon's thread pool we are safe
592 * to call methods on KeyChain directly.
594 * @return list containing the certificates (first element is the user certificate)
595 * @throws InterruptedException
596 * @throws KeyChainException
597 * @throws CertificateEncodingException
599 private byte[][] getUserCertificate() throws KeyChainException
, InterruptedException
, CertificateEncodingException
601 ArrayList
<byte[]> encodings
= new ArrayList
<byte[]>();
602 X509Certificate
[] chain
= KeyChain
.getCertificateChain(getApplicationContext(), mCurrentUserCertificateAlias
);
603 if (chain
== null
|| chain
.length
== 0)
607 for (X509Certificate cert
: chain
)
609 encodings
.add(cert
.getEncoded());
611 return encodings
.toArray(new byte[encodings
.size()][]);
615 * Function called via JNI to get the private key the user selected.
617 * Since this method is called from a thread of charon's thread pool we are safe
618 * to call methods on KeyChain directly.
620 * @return the private key
621 * @throws InterruptedException
622 * @throws KeyChainException
623 * @throws CertificateEncodingException
625 private PrivateKey
getUserKey() throws KeyChainException
, InterruptedException
627 return KeyChain
.getPrivateKey(getApplicationContext(), mCurrentUserCertificateAlias
);
631 * Initialization of charon, provided by libandroidbridge.so
633 * @param builder BuilderAdapter for this connection
634 * @param logfile absolute path to the logfile
635 * @param byod enable BYOD features
636 * @return TRUE if initialization was successful
638 public native boolean initializeCharon(BuilderAdapter builder
, String logfile
, boolean byod
);
641 * Deinitialize charon, provided by libandroidbridge.so
643 public native void deinitializeCharon();
646 * Initiate VPN, provided by libandroidbridge.so
648 public native void initiate(String config
);
651 * Adapter for VpnService.Builder which is used to access it safely via JNI.
652 * There is a corresponding C object to access it from native code.
654 public class BuilderAdapter
656 private final VpnProfile mProfile
;
657 private VpnService
.Builder mBuilder
;
658 private BuilderCache mCache
;
659 private BuilderCache mEstablishedCache
;
661 public BuilderAdapter(VpnProfile profile
)
664 mBuilder
= createBuilder(mProfile
.getName());
665 mCache
= new BuilderCache(mProfile
);
668 private VpnService
.Builder
createBuilder(String name
)
670 VpnService
.Builder builder
= new CharonVpnService
.Builder();
671 builder
.setSession(name
);
673 /* even though the option displayed in the system dialog says "Configure"
674 * we just use our main Activity */
675 Context context
= getApplicationContext();
676 Intent intent
= new Intent(context
, MainActivity
.class);
677 PendingIntent pending
= PendingIntent
.getActivity(context
, 0, intent
,
678 PendingIntent
.FLAG_UPDATE_CURRENT
);
679 builder
.setConfigureIntent(pending
);
683 public synchronized boolean addAddress(String address
, int prefixLength
)
687 mCache
.addAddress(address
, prefixLength
);
689 catch (IllegalArgumentException ex
)
696 public synchronized boolean addDnsServer(String address
)
700 mBuilder
.addDnsServer(address
);
701 mCache
.recordAddressFamily(address
);
703 catch (IllegalArgumentException ex
)
710 public synchronized boolean addRoute(String address
, int prefixLength
)
714 mCache
.addRoute(address
, prefixLength
);
716 catch (IllegalArgumentException ex
)
723 public synchronized boolean addSearchDomain(String domain
)
727 mBuilder
.addSearchDomain(domain
);
729 catch (IllegalArgumentException ex
)
736 public synchronized boolean setMtu(int mtu
)
742 catch (IllegalArgumentException ex
)
749 public synchronized int establish()
751 ParcelFileDescriptor fd
;
754 mCache
.applyData(mBuilder
);
755 fd
= mBuilder
.establish();
759 ex
.printStackTrace();
766 /* now that the TUN device is created we don't need the current
767 * builder anymore, but we might need another when reestablishing */
768 mBuilder
= createBuilder(mProfile
.getName());
769 mEstablishedCache
= mCache
;
770 mCache
= new BuilderCache(mProfile
);
771 return fd
.detachFd();
774 public synchronized int establishNoDns()
776 ParcelFileDescriptor fd
;
778 if (mEstablishedCache
== null
)
784 Builder builder
= createBuilder(mProfile
.getName());
785 mEstablishedCache
.applyData(builder
);
786 fd
= builder
.establish();
790 ex
.printStackTrace();
797 return fd
.detachFd();
802 * Cache non DNS related information so we can recreate the builder without
803 * that information when reestablishing IKE_SAs
805 public class BuilderCache
807 private final List
<IPRange
> mAddresses
= new ArrayList
<>();
808 private final List
<IPRange
> mRoutesIPv4
= new ArrayList
<>();
809 private final List
<IPRange
> mRoutesIPv6
= new ArrayList
<>();
810 private final IPRangeSet mIncludedSubnetsv4
= new IPRangeSet();
811 private final IPRangeSet mIncludedSubnetsv6
= new IPRangeSet();
812 private final IPRangeSet mExcludedSubnets
;
813 private final int mSplitTunneling
;
814 private final SelectedAppsHandling mAppHandling
;
815 private final SortedSet
<String
> mSelectedApps
;
817 private boolean mIPv4Seen
, mIPv6Seen
;
819 public BuilderCache(VpnProfile profile
)
821 IPRangeSet included
= IPRangeSet
.fromString(profile
.getIncludedSubnets());
822 for (IPRange range
: included
)
824 if (range
.getFrom() instanceof Inet4Address
)
826 mIncludedSubnetsv4
.add(range
);
828 else if (range
.getFrom() instanceof Inet6Address
)
830 mIncludedSubnetsv6
.add(range
);
833 mExcludedSubnets
= IPRangeSet
.fromString(profile
.getExcludedSubnets());
834 Integer splitTunneling
= profile
.getSplitTunneling();
835 mSplitTunneling
= splitTunneling
!= null ? splitTunneling
: 0;
836 mAppHandling
= profile
.getSelectedAppsHandling();
837 mSelectedApps
= profile
.getSelectedAppsSet();
840 public void addAddress(String address
, int prefixLength
)
844 mAddresses
.add(new IPRange(address
, prefixLength
));
845 recordAddressFamily(address
);
847 catch (UnknownHostException ex
)
849 ex
.printStackTrace();
853 public void addRoute(String address
, int prefixLength
)
859 mRoutesIPv6
.add(new IPRange(address
, prefixLength
));
863 mRoutesIPv4
.add(new IPRange(address
, prefixLength
));
866 catch (UnknownHostException ex
)
868 ex
.printStackTrace();
872 public void setMtu(int mtu
)
877 public void recordAddressFamily(String address
)
890 catch (UnknownHostException ex
)
892 ex
.printStackTrace();
896 @TargetApi(Build
.VERSION_CODES
.LOLLIPOP
)
897 public void applyData(VpnService
.Builder builder
)
899 for (IPRange address
: mAddresses
)
901 builder
.addAddress(address
.getFrom(), address
.getPrefix());
903 /* add routes depending on whether split tunneling is allowed or not,
904 * that is, whether we have to handle and block non-VPN traffic */
905 if ((mSplitTunneling
& VpnProfile
.SPLIT_TUNNELING_BLOCK_IPV4
) == 0)
908 { /* split tunneling is used depending on the routes and configuration */
909 IPRangeSet ranges
= new IPRangeSet();
910 if (mIncludedSubnetsv4
.size() > 0)
912 ranges
.add(mIncludedSubnetsv4
);
916 ranges
.addAll(mRoutesIPv4
);
918 ranges
.remove(mExcludedSubnets
);
919 for (IPRange subnet
: ranges
.subnets())
921 builder
.addRoute(subnet
.getFrom(), subnet
.getPrefix());
924 else if (Build
.VERSION
.SDK_INT
>= Build
.VERSION_CODES
.LOLLIPOP
)
925 { /* allow traffic that would otherwise be blocked to bypass the VPN */
926 builder
.allowFamily(OsConstants
.AF_INET
);
930 { /* only needed if we've seen any addresses. otherwise, traffic
931 * is blocked by default (we also install no routes in that case) */
932 builder
.addRoute("0.0.0.0", 0);
934 /* same thing for IPv6 */
935 if ((mSplitTunneling
& VpnProfile
.SPLIT_TUNNELING_BLOCK_IPV6
) == 0)
939 IPRangeSet ranges
= new IPRangeSet();
940 if (mIncludedSubnetsv6
.size() > 0)
942 ranges
.add(mIncludedSubnetsv6
);
946 ranges
.addAll(mRoutesIPv6
);
948 ranges
.remove(mExcludedSubnets
);
949 for (IPRange subnet
: ranges
.subnets())
951 builder
.addRoute(subnet
.getFrom(), subnet
.getPrefix());
954 else if (Build
.VERSION
.SDK_INT
>= Build
.VERSION_CODES
.LOLLIPOP
)
956 builder
.allowFamily(OsConstants
.AF_INET6
);
961 builder
.addRoute("::", 0);
963 /* apply selected applications */
964 if (mSelectedApps
.size() > 0)
966 switch (mAppHandling
)
968 case SELECTED_APPS_EXCLUDE
:
969 for (String app
: mSelectedApps
)
973 builder
.addDisallowedApplication(app
);
975 catch (PackageManager
.NameNotFoundException e
)
977 // possible if not configured via GUI or app was uninstalled
981 case SELECTED_APPS_ONLY
:
982 for (String app
: mSelectedApps
)
986 builder
.addAllowedApplication(app
);
988 catch (PackageManager
.NameNotFoundException e
)
990 // possible if not configured via GUI or app was uninstalled
998 builder
.setMtu(mMtu
);
1001 private boolean isIPv6(String address
) throws UnknownHostException
1003 InetAddress addr
= InetAddress
.getByName(address
);
1004 if (addr
instanceof Inet4Address
)
1008 else if (addr
instanceof Inet6Address
)
1017 * Function called via JNI to determine information about the Android version.
1019 private static String
getAndroidVersion()
1021 return "Android " + Build
.VERSION
.RELEASE
+ " - " + Build
.DISPLAY
+
1022 "/" + Build
.VERSION
.SECURITY_PATCH
;
1026 * Function called via JNI to determine information about the device.
1028 private static String
getDeviceString()
1030 return Build
.MODEL
+ " - " + Build
.BRAND
+ "/" + Build
.PRODUCT
+ "/" + Build
.MANUFACTURER
;
1034 * The libraries are extracted to /data/data/org.strongswan.android/...
1035 * during installation. On newer releases most are loaded in JNI_OnLoad.
1039 if (Build
.VERSION
.SDK_INT
< Build
.VERSION_CODES
.JELLY_BEAN_MR2
)
1041 System
.loadLibrary("strongswan");
1043 if (MainActivity
.USE_BYOD
)
1045 System
.loadLibrary("tpmtss");
1046 System
.loadLibrary("tncif");
1047 System
.loadLibrary("tnccs");
1048 System
.loadLibrary("imcv");
1051 System
.loadLibrary("charon");
1052 System
.loadLibrary("ipsec");
1054 System
.loadLibrary("androidbridge");