]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java
android: Remove unnecessary API checks
[thirdparty/strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / logic / CharonVpnService.java
CommitLineData
a4f9028e 1/*
6ca0b46c 2 * Copyright (C) 2012-2018 Tobias Brunner
a4f9028e
TB
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
19ef2aec
TB
5 *
6 * Copyright (C) secunet Security Networks AG
a4f9028e
TB
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
8bf30276 19package org.strongswan.android.logic;
4a208143 20
acc8948f 21import android.annotation.TargetApi;
d5070425 22import android.app.Notification;
6ca0b46c 23import android.app.NotificationChannel;
d5070425 24import android.app.NotificationManager;
9c0be3ac 25import android.app.PendingIntent;
03de55ad
TB
26import android.app.Service;
27import android.content.ComponentName;
9c0be3ac 28import android.content.Context;
4a208143 29import android.content.Intent;
03de55ad 30import android.content.ServiceConnection;
08c79d51 31import android.content.SharedPreferences;
f2e7156d 32import android.content.pm.PackageManager;
4a208143 33import android.net.VpnService;
69e0215b 34import android.os.Build;
a4f9028e 35import android.os.Bundle;
70d6a0cf 36import android.os.Handler;
03de55ad 37import android.os.IBinder;
5215d512 38import android.os.ParcelFileDescriptor;
3aba3386
TB
39import android.security.KeyChain;
40import android.security.KeyChainException;
acc8948f 41import android.system.OsConstants;
a4f9028e 42import android.util.Log;
4a208143 43
d5070425
TB
44import org.strongswan.android.R;
45import org.strongswan.android.data.VpnProfile;
f2e7156d 46import org.strongswan.android.data.VpnProfile.SelectedAppsHandling;
d5070425
TB
47import org.strongswan.android.data.VpnProfileDataSource;
48import org.strongswan.android.data.VpnType.VpnTypeFeature;
49import org.strongswan.android.logic.VpnStateService.ErrorState;
50import org.strongswan.android.logic.VpnStateService.State;
51import org.strongswan.android.logic.imc.ImcState;
52import org.strongswan.android.logic.imc.RemediationInstruction;
53import org.strongswan.android.ui.MainActivity;
58d139da 54import org.strongswan.android.ui.VpnProfileControlActivity;
08c79d51 55import org.strongswan.android.utils.Constants;
72b7c289
TB
56import org.strongswan.android.utils.IPRange;
57import org.strongswan.android.utils.IPRangeSet;
d5070425 58import org.strongswan.android.utils.SettingsWriter;
2ef473be 59import org.strongswan.android.utils.Utils;
d5070425
TB
60
61import java.io.File;
fe1f1432
TB
62import java.io.FileInputStream;
63import java.io.IOException;
d5070425
TB
64import java.net.Inet4Address;
65import java.net.Inet6Address;
66import java.net.InetAddress;
67import java.net.UnknownHostException;
fe1f1432
TB
68import java.nio.ByteBuffer;
69import java.nio.channels.ClosedByInterruptException;
d5070425
TB
70import java.security.PrivateKey;
71import java.security.cert.CertificateEncodingException;
72import java.security.cert.X509Certificate;
73import java.util.ArrayList;
74import java.util.List;
75import java.util.Locale;
800f881a 76import java.util.SortedSet;
d5070425 77
3b9696fc
TB
78import androidx.core.app.NotificationCompat;
79import androidx.core.content.ContextCompat;
e106fce4 80import androidx.preference.PreferenceManager;
3b9696fc 81
d5070425 82public class CharonVpnService extends VpnService implements Runnable, VpnStateService.VpnStateListener
a4057603 83{
a4f9028e 84 private static final String TAG = CharonVpnService.class.getSimpleName();
56f59956 85 private static final String VPN_SERVICE_ACTION = "android.net.VpnService";
59693d6c 86 public static final String DISCONNECT_ACTION = "org.strongswan.android.CharonVpnService.DISCONNECT";
6ca0b46c 87 private static final String NOTIFICATION_CHANNEL = "org.strongswan.android.CharonVpnService.VPN_STATE_NOTIFICATION";
fe05f1f0 88 public static final String LOG_FILE = "charon.log";
fb3772ec 89 public static final String KEY_IS_RETRY = "retry";
d5070425 90 public static final int VPN_STATE_NOTIFICATION_ID = 1;
fe05f1f0
TB
91
92 private String mLogFile;
3fe9a436 93 private String mAppDir;
a4f9028e
TB
94 private VpnProfileDataSource mDataSource;
95 private Thread mConnectionHandler;
96 private VpnProfile mCurrentProfile;
9d0f8a3a 97 private volatile String mCurrentCertificateAlias;
3aba3386 98 private volatile String mCurrentUserCertificateAlias;
a4f9028e
TB
99 private VpnProfile mNextProfile;
100 private volatile boolean mProfileUpdated;
101 private volatile boolean mTerminate;
a7c8b166 102 private volatile boolean mIsDisconnecting;
d5070425 103 private volatile boolean mShowNotification;
a3e895b4 104 private final BuilderAdapter mBuilderAdapter = new BuilderAdapter();
70d6a0cf 105 private Handler mHandler;
03de55ad
TB
106 private VpnStateService mService;
107 private final Object mServiceLock = new Object();
a3e895b4
MP
108 private final ServiceConnection mServiceConnection = new ServiceConnection()
109 {
03de55ad
TB
110 @Override
111 public void onServiceDisconnected(ComponentName name)
112 { /* since the service is local this is theoretically only called when the process is terminated */
113 synchronized (mServiceLock)
114 {
115 mService = null;
116 }
117 }
118
119 @Override
120 public void onServiceConnected(ComponentName name, IBinder service)
121 {
122 synchronized (mServiceLock)
123 {
124 mService = ((VpnStateService.LocalBinder)service).getService();
125 }
126 /* we are now ready to start the handler thread */
d5070425 127 mService.registerListener(CharonVpnService.this);
03de55ad
TB
128 mConnectionHandler.start();
129 }
130 };
4a208143 131
8c2af60c
TB
132 /**
133 * as defined in charonservice.h
134 */
135 static final int STATE_CHILD_SA_UP = 1;
136 static final int STATE_CHILD_SA_DOWN = 2;
137 static final int STATE_AUTH_ERROR = 3;
138 static final int STATE_PEER_AUTH_ERROR = 4;
139 static final int STATE_LOOKUP_ERROR = 5;
140 static final int STATE_UNREACHABLE_ERROR = 6;
ab5dbbc4
TB
141 static final int STATE_CERTIFICATE_UNAVAILABLE = 7;
142 static final int STATE_GENERIC_ERROR = 8;
8c2af60c 143
4a208143 144 @Override
a4057603
TB
145 public int onStartCommand(Intent intent, int flags, int startId)
146 {
a4f9028e
TB
147 if (intent != null)
148 {
56f59956 149 VpnProfile profile = null;
fb3772ec 150 boolean retry = false;
56f59956
TB
151
152 if (VPN_SERVICE_ACTION.equals(intent.getAction()))
153 { /* triggered when Always-on VPN is activated */
154 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
155 String uuid = pref.getString(Constants.PREF_DEFAULT_VPN_PROFILE, null);
156 if (uuid == null || uuid.equals(Constants.PREF_DEFAULT_VPN_PROFILE_MRU))
157 {
158 uuid = pref.getString(Constants.PREF_MRU_VPN_PROFILE, null);
159 }
160 profile = mDataSource.getVpnProfile(uuid);
59693d6c 161 }
56f59956 162 else if (!DISCONNECT_ACTION.equals(intent.getAction()))
59693d6c
TB
163 {
164 Bundle bundle = intent.getExtras();
59693d6c 165 if (bundle != null)
a4f9028e 166 {
6f9b96ac 167 profile = mDataSource.getVpnProfile(bundle.getString(VpnProfileDataSource.KEY_UUID));
59693d6c
TB
168 if (profile != null)
169 {
170 String password = bundle.getString(VpnProfileDataSource.KEY_PASSWORD);
171 profile.setPassword(password);
08c79d51 172
fb3772ec
TB
173 retry = bundle.getBoolean(CharonVpnService.KEY_IS_RETRY, false);
174
08c79d51
TB
175 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
176 pref.edit().putString(Constants.PREF_MRU_VPN_PROFILE, profile.getUUID().toString())
177 .apply();
59693d6c 178 }
a4f9028e
TB
179 }
180 }
fb3772ec
TB
181 if (profile != null && !retry)
182 { /* delete the log file if this is not an automatic retry */
183 deleteFile(LOG_FILE);
184 }
56f59956 185 setNextProfile(profile);
a4f9028e
TB
186 }
187 return START_NOT_STICKY;
4a208143
TB
188 }
189
190 @Override
a4057603
TB
191 public void onCreate()
192 {
fe05f1f0 193 mLogFile = getFilesDir().getAbsolutePath() + File.separator + LOG_FILE;
3fe9a436 194 mAppDir = getFilesDir().getAbsolutePath();
fe05f1f0 195
70d6a0cf 196 /* handler used to do changes in the main UI thread */
dc351a30 197 mHandler = new Handler(getMainLooper());
70d6a0cf 198
a4f9028e
TB
199 mDataSource = new VpnProfileDataSource(this);
200 mDataSource.open();
201 /* use a separate thread as main thread for charon */
202 mConnectionHandler = new Thread(this);
03de55ad
TB
203 /* the thread is started when the service is bound */
204 bindService(new Intent(this, VpnStateService.class),
205 mServiceConnection, Service.BIND_AUTO_CREATE);
6ca0b46c
TB
206
207 createNotificationChannel();
a4f9028e
TB
208 }
209
210 @Override
211 public void onRevoke()
212 { /* the system revoked the rights grated with the initial prepare() call.
213 * called when the user clicks disconnect in the system's VPN dialog */
214 setNextProfile(null);
4a208143
TB
215 }
216
217 @Override
a4057603
TB
218 public void onDestroy()
219 {
a4f9028e
TB
220 mTerminate = true;
221 setNextProfile(null);
222 try
223 {
224 mConnectionHandler.join();
225 }
226 catch (InterruptedException e)
227 {
228 e.printStackTrace();
229 }
03de55ad
TB
230 if (mService != null)
231 {
d5070425 232 mService.unregisterListener(this);
03de55ad
TB
233 unbindService(mServiceConnection);
234 }
a4f9028e
TB
235 mDataSource.close();
236 }
237
238 /**
239 * Set the profile that is to be initiated next. Notify the handler thread.
240 *
241 * @param profile the profile to initiate
242 */
243 private void setNextProfile(VpnProfile profile)
244 {
245 synchronized (this)
246 {
247 this.mNextProfile = profile;
248 mProfileUpdated = true;
249 notifyAll();
250 }
251 }
252
253 @Override
254 public void run()
255 {
256 while (true)
257 {
258 synchronized (this)
259 {
260 try
261 {
262 while (!mProfileUpdated)
263 {
264 wait();
265 }
266
267 mProfileUpdated = false;
268 stopCurrentConnection();
269 if (mNextProfile == null)
270 {
03de55ad 271 setState(State.DISABLED);
a4f9028e
TB
272 if (mTerminate)
273 {
274 break;
275 }
276 }
277 else
278 {
279 mCurrentProfile = mNextProfile;
280 mNextProfile = null;
281
9d0f8a3a
TB
282 /* store this in a separate (volatile) variable to avoid
283 * a possible deadlock during deinitialization */
284 mCurrentCertificateAlias = mCurrentProfile.getCertificateAlias();
3aba3386 285 mCurrentUserCertificateAlias = mCurrentProfile.getUserCertificateAlias();
9d0f8a3a 286
38172313 287 startConnection(mCurrentProfile);
a7c8b166 288 mIsDisconnecting = false;
03de55ad 289
ad2d20e5 290 SimpleFetcher.enable();
d5070425 291 addNotification();
fe1f1432 292 mBuilderAdapter.setProfile(mCurrentProfile);
35819143
TB
293 if (initializeCharon(mBuilderAdapter, mLogFile, mAppDir, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD),
294 (mCurrentProfile.getFlags() & VpnProfile.FLAGS_IPv6_TRANSPORT) != 0))
c3ee829e
TB
295 {
296 Log.i(TAG, "charon started");
f0b3e303
TB
297
298 if (mCurrentProfile.getVpnType().has(VpnTypeFeature.USER_PASS) &&
299 mCurrentProfile.getPassword() == null)
300 { /* this can happen if Always-on VPN is enabled with an incomplete profile */
301 setError(ErrorState.PASSWORD_MISSING);
302 continue;
303 }
304
79af70c6 305 SettingsWriter writer = new SettingsWriter();
6830cb1c 306 writer.setValue("global.language", Locale.getDefault().getLanguage());
4d02c49e 307 writer.setValue("global.mtu", mCurrentProfile.getMTU());
db599d6b 308 writer.setValue("global.nat_keepalive", mCurrentProfile.getNATKeepAlive());
205ec47d 309 writer.setValue("global.rsa_pss", (mCurrentProfile.getFlags() & VpnProfile.FLAGS_RSA_PSS) != 0);
a7060581
TB
310 writer.setValue("global.crl", (mCurrentProfile.getFlags() & VpnProfile.FLAGS_DISABLE_CRL) == 0);
311 writer.setValue("global.ocsp", (mCurrentProfile.getFlags() & VpnProfile.FLAGS_DISABLE_OCSP) == 0);
79af70c6
TB
312 writer.setValue("connection.type", mCurrentProfile.getVpnType().getIdentifier());
313 writer.setValue("connection.server", mCurrentProfile.getGateway());
cda167c8 314 writer.setValue("connection.port", mCurrentProfile.getPort());
79af70c6
TB
315 writer.setValue("connection.username", mCurrentProfile.getUsername());
316 writer.setValue("connection.password", mCurrentProfile.getPassword());
9c556441
TB
317 writer.setValue("connection.local_id", mCurrentProfile.getLocalId());
318 writer.setValue("connection.remote_id", mCurrentProfile.getRemoteId());
3f0592d0 319 writer.setValue("connection.certreq", (mCurrentProfile.getFlags() & VpnProfile.FLAGS_SUPPRESS_CERT_REQS) == 0);
a7060581 320 writer.setValue("connection.strict_revocation", (mCurrentProfile.getFlags() & VpnProfile.FLAGS_STRICT_REVOCATION) != 0);
a7c43544
TB
321 writer.setValue("connection.ike_proposal", mCurrentProfile.getIkeProposal());
322 writer.setValue("connection.esp_proposal", mCurrentProfile.getEspProposal());
79af70c6 323 initiate(writer.serialize());
c3ee829e
TB
324 }
325 else
326 {
327 Log.e(TAG, "failed to start charon");
328 setError(ErrorState.GENERIC_ERROR);
329 setState(State.DISABLED);
330 mCurrentProfile = null;
331 }
a4f9028e
TB
332 }
333 }
334 catch (InterruptedException ex)
335 {
336 stopCurrentConnection();
03de55ad 337 setState(State.DISABLED);
a4f9028e
TB
338 }
339 }
340 }
341 }
342
343 /**
344 * Stop any existing connection by deinitializing charon.
345 */
346 private void stopCurrentConnection()
347 {
348 synchronized (this)
349 {
a3e895b4 350 if (mNextProfile != null)
fe1f1432
TB
351 {
352 mBuilderAdapter.setProfile(mNextProfile);
353 mBuilderAdapter.establishBlocking();
354 }
355
a4f9028e
TB
356 if (mCurrentProfile != null)
357 {
03de55ad 358 setState(State.DISCONNECTING);
a7c8b166 359 mIsDisconnecting = true;
ad2d20e5 360 SimpleFetcher.disable();
a4f9028e
TB
361 deinitializeCharon();
362 Log.i(TAG, "charon stopped");
363 mCurrentProfile = null;
bc528681 364 if (mNextProfile == null)
fe1f1432 365 { /* only do this if we are not connecting to another profile */
bc528681 366 removeNotification();
fe1f1432 367 mBuilderAdapter.closeBlocking();
bc528681 368 }
a4f9028e
TB
369 }
370 }
4a208143
TB
371 }
372
d5070425
TB
373 /**
374 * Add a permanent notification while we are connected to avoid the service getting killed by
375 * the system when low on memory.
376 */
377 private void addNotification()
378 {
70d6a0cf
TB
379 mHandler.post(new Runnable()
380 {
381 @Override
382 public void run()
383 {
384 mShowNotification = true;
385 startForeground(VPN_STATE_NOTIFICATION_ID, buildNotification(false));
386 }
387 });
d5070425
TB
388 }
389
390 /**
391 * Remove the permanent notification.
392 */
393 private void removeNotification()
394 {
70d6a0cf
TB
395 mHandler.post(new Runnable()
396 {
397 @Override
398 public void run()
399 {
400 mShowNotification = false;
401 stopForeground(true);
402 }
403 });
d5070425
TB
404 }
405
6ca0b46c
TB
406 /**
407 * Create a notification channel for Android 8+
408 */
409 private void createNotificationChannel()
410 {
411 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
412 {
413 NotificationChannel channel;
414 channel = new NotificationChannel(NOTIFICATION_CHANNEL, getString(R.string.permanent_notification_name),
415 NotificationManager.IMPORTANCE_LOW);
416 channel.setDescription(getString(R.string.permanent_notification_description));
417 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
418 channel.setShowBadge(false);
419 NotificationManager notificationManager = getSystemService(NotificationManager.class);
420 notificationManager.createNotificationChannel(channel);
421 }
422 }
423
424
d5070425
TB
425 /**
426 * Build a notification matching the current state
427 */
85059424 428 private Notification buildNotification(boolean publicVersion)
d5070425
TB
429 {
430 VpnProfile profile = mService.getProfile();
431 State state = mService.getState();
432 ErrorState error = mService.getErrorState();
433 String name = "";
64c2d3ca 434 boolean add_action = false;
d5070425
TB
435
436 if (profile != null)
437 {
438 name = profile.getName();
439 }
6ca0b46c 440 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL)
a3e895b4
MP
441 .setSmallIcon(R.drawable.ic_notification)
442 .setCategory(NotificationCompat.CATEGORY_SERVICE)
443 .setVisibility(publicVersion ? NotificationCompat.VISIBILITY_PUBLIC
444 : NotificationCompat.VISIBILITY_PRIVATE);
d5070425
TB
445 int s = R.string.state_disabled;
446 if (error != ErrorState.NO_ERROR)
447 {
a7d679ff 448 s = mService.getErrorText();
d5070425
TB
449 builder.setSmallIcon(R.drawable.ic_notification_warning);
450 builder.setColor(ContextCompat.getColor(this, R.color.error_text));
2ec6ad71
TB
451
452 if (!publicVersion && profile != null)
453 {
454 int retry = mService.getRetryIn();
455 if (retry > 0)
456 {
457 builder.setContentText(getResources().getQuantityString(R.plurals.retry_in, retry, retry));
458 builder.setProgress(mService.getRetryTimeout(), retry, false);
459 }
460
461 Intent intent = new Intent(getApplicationContext(), VpnProfileControlActivity.class);
462 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
463 intent.setAction(VpnProfileControlActivity.START_PROFILE);
464 intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
563407e4
TB
465 int flags = PendingIntent.FLAG_UPDATE_CURRENT;
466 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
467 {
468 flags |= PendingIntent.FLAG_IMMUTABLE;
469 }
2ec6ad71 470 PendingIntent pending = PendingIntent.getActivity(getApplicationContext(), 0, intent,
563407e4 471 flags);
2ec6ad71
TB
472 builder.addAction(R.drawable.ic_notification_connecting, getString(R.string.retry), pending);
473 add_action = true;
474 }
d5070425
TB
475 }
476 else
477 {
2ec6ad71
TB
478 builder.setProgress(0, 0, false);
479
d5070425
TB
480 switch (state)
481 {
482 case CONNECTING:
483 s = R.string.state_connecting;
52aaffde 484 builder.setSmallIcon(R.drawable.ic_notification_connecting);
d5070425 485 builder.setColor(ContextCompat.getColor(this, R.color.warning_text));
64c2d3ca 486 add_action = true;
d5070425
TB
487 break;
488 case CONNECTED:
489 s = R.string.state_connected;
490 builder.setColor(ContextCompat.getColor(this, R.color.success_text));
491 builder.setUsesChronometer(true);
64c2d3ca 492 add_action = true;
d5070425
TB
493 break;
494 case DISCONNECTING:
495 s = R.string.state_disconnecting;
496 break;
497 }
498 }
499 builder.setContentTitle(getString(s));
563407e4
TB
500
501 int flags = PendingIntent.FLAG_UPDATE_CURRENT;
502 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
503 {
504 flags |= PendingIntent.FLAG_IMMUTABLE;
505 }
85059424
TB
506 if (!publicVersion)
507 {
64c2d3ca
TB
508 if (add_action)
509 {
58d139da
TB
510 Intent intent = new Intent(getApplicationContext(), VpnProfileControlActivity.class);
511 intent.setAction(VpnProfileControlActivity.DISCONNECT);
64c2d3ca 512 PendingIntent pending = PendingIntent.getActivity(getApplicationContext(), 0, intent,
563407e4 513 flags);
64c2d3ca
TB
514 builder.addAction(R.drawable.ic_notification_disconnect, getString(R.string.disconnect), pending);
515 }
a7d679ff
TB
516 if (error == ErrorState.NO_ERROR)
517 {
518 builder.setContentText(name);
519 }
85059424
TB
520 builder.setPublicVersion(buildNotification(true));
521 }
d5070425
TB
522
523 Intent intent = new Intent(getApplicationContext(), MainActivity.class);
524 PendingIntent pending = PendingIntent.getActivity(getApplicationContext(), 0, intent,
563407e4 525 flags);
d5070425
TB
526 builder.setContentIntent(pending);
527 return builder.build();
528 }
529
530 @Override
a3e895b4
MP
531 public void stateChanged()
532 {
d5070425
TB
533 if (mShowNotification)
534 {
a3e895b4 535 NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
85059424 536 manager.notify(VPN_STATE_NOTIFICATION_ID, buildNotification(false));
d5070425
TB
537 }
538 }
539
03de55ad 540 /**
38172313
TB
541 * Notify the state service about a new connection attempt.
542 * Called by the handler thread.
03de55ad
TB
543 *
544 * @param profile currently active VPN profile
545 */
38172313 546 private void startConnection(VpnProfile profile)
03de55ad
TB
547 {
548 synchronized (mServiceLock)
549 {
550 if (mService != null)
551 {
38172313 552 mService.startConnection(profile);
03de55ad
TB
553 }
554 }
555 }
556
557 /**
558 * Update the current VPN state on the state service. Called by the handler
559 * thread and any of charon's threads.
560 *
561 * @param state current state
562 */
563 private void setState(State state)
564 {
565 synchronized (mServiceLock)
566 {
567 if (mService != null)
568 {
569 mService.setState(state);
570 }
571 }
572 }
573
574 /**
575 * Set an error on the state service. Called by the handler thread and any
576 * of charon's threads.
577 *
578 * @param error error state
579 */
580 private void setError(ErrorState error)
581 {
582 synchronized (mServiceLock)
583 {
584 if (mService != null)
585 {
586 mService.setError(error);
587 }
588 }
589 }
590
dc52cfab
TB
591 /**
592 * Set the IMC state on the state service. Called by the handler thread and
593 * any of charon's threads.
594 *
595 * @param state IMC state
596 */
597 private void setImcState(ImcState state)
598 {
599 synchronized (mServiceLock)
600 {
601 if (mService != null)
602 {
603 mService.setImcState(state);
604 }
605 }
606 }
607
8c2af60c 608 /**
394be2d5
TB
609 * Set an error on the state service. Called by the handler thread and any
610 * of charon's threads.
8c2af60c
TB
611 *
612 * @param error error state
613 */
614 private void setErrorDisconnect(ErrorState error)
615 {
616 synchronized (mServiceLock)
617 {
618 if (mService != null)
619 {
a7c8b166
TB
620 if (!mIsDisconnecting)
621 {
38172313 622 mService.setError(error);
a7c8b166 623 }
8c2af60c
TB
624 }
625 }
626 }
627
628 /**
629 * Updates the state of the current connection.
630 * Called via JNI by different threads (but not concurrently).
631 *
632 * @param status new state
633 */
634 public void updateStatus(int status)
635 {
636 switch (status)
637 {
638 case STATE_CHILD_SA_DOWN:
1435bd2e
TB
639 if (!mIsDisconnecting)
640 {
641 setState(State.CONNECTING);
642 }
8c2af60c
TB
643 break;
644 case STATE_CHILD_SA_UP:
645 setState(State.CONNECTED);
646 break;
647 case STATE_AUTH_ERROR:
648 setErrorDisconnect(ErrorState.AUTH_FAILED);
649 break;
650 case STATE_PEER_AUTH_ERROR:
651 setErrorDisconnect(ErrorState.PEER_AUTH_FAILED);
652 break;
653 case STATE_LOOKUP_ERROR:
654 setErrorDisconnect(ErrorState.LOOKUP_FAILED);
655 break;
656 case STATE_UNREACHABLE_ERROR:
657 setErrorDisconnect(ErrorState.UNREACHABLE);
658 break;
ab5dbbc4
TB
659 case STATE_CERTIFICATE_UNAVAILABLE:
660 setErrorDisconnect(ErrorState.CERTIFICATE_UNAVAILABLE);
661 break;
8c2af60c
TB
662 case STATE_GENERIC_ERROR:
663 setErrorDisconnect(ErrorState.GENERIC_ERROR);
664 break;
665 default:
666 Log.e(TAG, "Unknown status code received");
667 break;
668 }
669 }
670
dc52cfab
TB
671 /**
672 * Updates the IMC state of the current connection.
673 * Called via JNI by different threads (but not concurrently).
674 *
675 * @param value new state
676 */
677 public void updateImcState(int value)
678 {
679 ImcState state = ImcState.fromValue(value);
680 if (state != null)
681 {
682 setImcState(state);
683 }
684 }
685
a05acd76
TB
686 /**
687 * Add a remediation instruction to the VPN state service.
688 * Called via JNI by different threads (but not concurrently).
689 *
690 * @param xml XML text
691 */
692 public void addRemediationInstruction(String xml)
693 {
694 for (RemediationInstruction instruction : RemediationInstruction.fromXml(xml))
695 {
696 synchronized (mServiceLock)
697 {
698 if (mService != null)
699 {
700 mService.addRemediationInstruction(instruction);
701 }
702 }
703 }
704 }
705
2bec193a
TB
706 /**
707 * Function called via JNI to generate a list of DER encoded CA certificates
708 * as byte array.
709 *
2bec193a
TB
710 * @return a list of DER encoded CA certificates
711 */
9d994ba5 712 private byte[][] getTrustedCertificates()
2bec193a
TB
713 {
714 ArrayList<byte[]> certs = new ArrayList<byte[]>();
3e85b5a4 715 TrustedCertificateManager certman = TrustedCertificateManager.getInstance().load();
2bec193a
TB
716 try
717 {
9d994ba5
TB
718 String alias = this.mCurrentCertificateAlias;
719 if (alias != null)
2bec193a 720 {
2bec193a
TB
721 X509Certificate cert = certman.getCACertificateFromAlias(alias);
722 if (cert == null)
2bec193a
TB
723 {
724 return null;
725 }
726 certs.add(cert.getEncoded());
727 }
728 else
729 {
9d994ba5 730 for (X509Certificate cert : certman.getAllCACertificates().values())
2bec193a 731 {
2bec193a
TB
732 certs.add(cert.getEncoded());
733 }
2bec193a
TB
734 }
735 }
736 catch (CertificateEncodingException e)
737 {
738 e.printStackTrace();
739 return null;
740 }
741 return certs.toArray(new byte[certs.size()][]);
742 }
743
3aba3386 744 /**
64595464
TB
745 * Function called via JNI to get a list containing the DER encoded certificates
746 * of the user selected certificate chain (beginning with the user certificate).
3aba3386
TB
747 *
748 * Since this method is called from a thread of charon's thread pool we are safe
749 * to call methods on KeyChain directly.
750 *
64595464 751 * @return list containing the certificates (first element is the user certificate)
3aba3386
TB
752 * @throws InterruptedException
753 * @throws KeyChainException
754 * @throws CertificateEncodingException
755 */
756 private byte[][] getUserCertificate() throws KeyChainException, InterruptedException, CertificateEncodingException
757 {
758 ArrayList<byte[]> encodings = new ArrayList<byte[]>();
64595464 759 X509Certificate[] chain = KeyChain.getCertificateChain(getApplicationContext(), mCurrentUserCertificateAlias);
3aba3386
TB
760 if (chain == null || chain.length == 0)
761 {
762 return null;
763 }
764 for (X509Certificate cert : chain)
765 {
766 encodings.add(cert.getEncoded());
767 }
768 return encodings.toArray(new byte[encodings.size()][]);
769 }
770
406d680e
TB
771 /**
772 * Function called via JNI to get the private key the user selected.
773 *
774 * Since this method is called from a thread of charon's thread pool we are safe
775 * to call methods on KeyChain directly.
776 *
777 * @return the private key
778 * @throws InterruptedException
779 * @throws KeyChainException
406d680e
TB
780 */
781 private PrivateKey getUserKey() throws KeyChainException, InterruptedException
782 {
783 return KeyChain.getPrivateKey(getApplicationContext(), mCurrentUserCertificateAlias);
406d680e
TB
784 }
785
4a208143
TB
786 /**
787 * Initialization of charon, provided by libandroidbridge.so
ae4f1ea1
TB
788 *
789 * @param builder BuilderAdapter for this connection
fe05f1f0 790 * @param logfile absolute path to the logfile
3fe9a436 791 * @param appdir absolute path to the data directory of the app
d5070425 792 * @param byod enable BYOD features
35819143 793 * @param ipv6 enable IPv6 transport
c3ee829e 794 * @return TRUE if initialization was successful
4a208143 795 */
35819143 796 public native boolean initializeCharon(BuilderAdapter builder, String logfile, String appdir, boolean byod, boolean ipv6);
4a208143
TB
797
798 /**
799 * Deinitialize charon, provided by libandroidbridge.so
800 */
801 public native void deinitializeCharon();
802
c6c39c78
TB
803 /**
804 * Initiate VPN, provided by libandroidbridge.so
805 */
79af70c6 806 public native void initiate(String config);
dffee9e2 807
5215d512
TB
808 /**
809 * Adapter for VpnService.Builder which is used to access it safely via JNI.
ae4f1ea1 810 * There is a corresponding C object to access it from native code.
5215d512
TB
811 */
812 public class BuilderAdapter
813 {
fe1f1432 814 private VpnProfile mProfile;
0326ceda 815 private VpnService.Builder mBuilder;
36aab70a
TB
816 private BuilderCache mCache;
817 private BuilderCache mEstablishedCache;
a3e895b4 818 private final PacketDropper mDropper = new PacketDropper();
5215d512 819
fe1f1432 820 public synchronized void setProfile(VpnProfile profile)
5215d512 821 {
f2e7156d
TB
822 mProfile = profile;
823 mBuilder = createBuilder(mProfile.getName());
824 mCache = new BuilderCache(mProfile);
0326ceda
TB
825 }
826
827 private VpnService.Builder createBuilder(String name)
828 {
829 VpnService.Builder builder = new CharonVpnService.Builder();
f2e7156d 830 builder.setSession(name);
9c0be3ac
TB
831
832 /* even though the option displayed in the system dialog says "Configure"
833 * we just use our main Activity */
834 Context context = getApplicationContext();
835 Intent intent = new Intent(context, MainActivity.class);
563407e4
TB
836 int flags = PendingIntent.FLAG_UPDATE_CURRENT;
837 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
838 {
839 flags |= PendingIntent.FLAG_IMMUTABLE;
840 }
841 PendingIntent pending = PendingIntent.getActivity(context, 0, intent, flags);
9c0be3ac 842 builder.setConfigureIntent(pending);
b32a9be4
TB
843
844 /* mark all VPN connections as unmetered (default changed for Android 10) */
845 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
846 {
847 builder.setMetered(false);
848 }
0326ceda 849 return builder;
5215d512
TB
850 }
851
852 public synchronized boolean addAddress(String address, int prefixLength)
853 {
854 try
855 {
36aab70a 856 mCache.addAddress(address, prefixLength);
5215d512
TB
857 }
858 catch (IllegalArgumentException ex)
859 {
860 return false;
861 }
862 return true;
863 }
864
865 public synchronized boolean addDnsServer(String address)
866 {
867 try
868 {
dd5de792 869 mCache.addDnsServer(address);
5215d512
TB
870 }
871 catch (IllegalArgumentException ex)
872 {
873 return false;
874 }
875 return true;
876 }
877
878 public synchronized boolean addRoute(String address, int prefixLength)
879 {
880 try
881 {
36aab70a 882 mCache.addRoute(address, prefixLength);
5215d512
TB
883 }
884 catch (IllegalArgumentException ex)
885 {
886 return false;
887 }
888 return true;
889 }
890
891 public synchronized boolean addSearchDomain(String domain)
892 {
893 try
894 {
0326ceda 895 mBuilder.addSearchDomain(domain);
5215d512
TB
896 }
897 catch (IllegalArgumentException ex)
898 {
899 return false;
900 }
901 return true;
902 }
903
904 public synchronized boolean setMtu(int mtu)
905 {
906 try
907 {
36aab70a 908 mCache.setMtu(mtu);
5215d512
TB
909 }
910 catch (IllegalArgumentException ex)
911 {
912 return false;
913 }
914 return true;
915 }
916
fe1f1432 917 private synchronized ParcelFileDescriptor establishIntern()
5215d512
TB
918 {
919 ParcelFileDescriptor fd;
920 try
921 {
acc8948f 922 mCache.applyData(mBuilder);
0326ceda 923 fd = mBuilder.establish();
fe1f1432
TB
924 if (fd != null)
925 {
926 closeBlocking();
927 }
5215d512
TB
928 }
929 catch (Exception ex)
930 {
931 ex.printStackTrace();
fe1f1432 932 return null;
5215d512
TB
933 }
934 if (fd == null)
935 {
fe1f1432 936 return null;
5215d512 937 }
0326ceda
TB
938 /* now that the TUN device is created we don't need the current
939 * builder anymore, but we might need another when reestablishing */
f2e7156d 940 mBuilder = createBuilder(mProfile.getName());
36aab70a 941 mEstablishedCache = mCache;
f2e7156d 942 mCache = new BuilderCache(mProfile);
fe1f1432
TB
943 return fd;
944 }
945
946 public synchronized int establish()
947 {
948 ParcelFileDescriptor fd = establishIntern();
949 return fd != null ? fd.detachFd() : -1;
950 }
951
952 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
953 public synchronized void establishBlocking()
954 {
955 /* just choose some arbitrary values to block all traffic (except for what's configured in the profile) */
956 mCache.addAddress("172.16.252.1", 32);
957 mCache.addAddress("fd00::fd02:1", 128);
958 mCache.addRoute("0.0.0.0", 0);
959 mCache.addRoute("::", 0);
84ee9577
TB
960 /* set DNS servers to avoid DNS leak later */
961 mBuilder.addDnsServer("8.8.8.8");
962 mBuilder.addDnsServer("2001:4860:4860::8888");
fe1f1432
TB
963 /* use blocking mode to simplify packet dropping */
964 mBuilder.setBlocking(true);
965 ParcelFileDescriptor fd = establishIntern();
966 if (fd != null)
967 {
968 mDropper.start(fd);
969 }
970 }
971
972 public synchronized void closeBlocking()
973 {
974 mDropper.stop();
5215d512 975 }
36aab70a
TB
976
977 public synchronized int establishNoDns()
978 {
979 ParcelFileDescriptor fd;
980
981 if (mEstablishedCache == null)
982 {
983 return -1;
984 }
985 try
986 {
f2e7156d 987 Builder builder = createBuilder(mProfile.getName());
36aab70a
TB
988 mEstablishedCache.applyData(builder);
989 fd = builder.establish();
990 }
991 catch (Exception ex)
992 {
993 ex.printStackTrace();
994 return -1;
995 }
996 if (fd == null)
997 {
998 return -1;
999 }
1000 return fd.detachFd();
1001 }
fe1f1432
TB
1002
1003 private class PacketDropper implements Runnable
1004 {
1005 private ParcelFileDescriptor mFd;
1006 private Thread mThread;
1007
1008 public void start(ParcelFileDescriptor fd)
1009 {
1010 mFd = fd;
1011 mThread = new Thread(this);
1012 mThread.start();
1013 }
1014
1015 public void stop()
1016 {
1017 if (mFd != null)
1018 {
1019 try
1020 {
1021 mThread.interrupt();
1022 mThread.join();
1023 mFd.close();
1024 }
1025 catch (InterruptedException e)
1026 {
1027 e.printStackTrace();
1028 }
1029 catch (IOException e)
1030 {
1031 e.printStackTrace();
1032 }
1033 mFd = null;
1034 }
1035 }
1036
1037 @Override
1038 public synchronized void run()
1039 {
1040 try
1041 {
1042 FileInputStream plain = new FileInputStream(mFd.getFileDescriptor());
1043 ByteBuffer packet = ByteBuffer.allocate(mCache.mMtu);
1044 while (true)
4e905b96
TB
1045 {
1046 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
1047 { /* just read and ignore all data, regular read() is not interruptible */
1048 int len = plain.getChannel().read(packet);
1049 packet.clear();
1050 if (len < 0)
1051 {
1052 break;
1053 }
1054 }
1055 else
1056 { /* this is rather ugly but on older platforms not even the NIO version of read() is interruptible */
1057 boolean wait = true;
1058 if (plain.available() > 0)
1059 {
1060 int len = plain.read(packet.array());
1061 packet.clear();
1062 if (len < 0 || Thread.interrupted())
1063 {
1064 break;
1065 }
1066 /* check again right away, there may be another packet */
1067 wait = false;
1068 }
1069 if (wait)
1070 {
1071 Thread.sleep(250);
1072 }
fe1f1432
TB
1073 }
1074 }
1075 }
a3e895b4 1076 catch (ClosedByInterruptException | InterruptedException e)
fe1f1432
TB
1077 {
1078 /* regular interruption */
1079 }
4e905b96 1080 catch (IOException e)
fe1f1432
TB
1081 {
1082 e.printStackTrace();
1083 }
1084 }
1085 }
36aab70a
TB
1086 }
1087
1088 /**
1089 * Cache non DNS related information so we can recreate the builder without
1090 * that information when reestablishing IKE_SAs
1091 */
1092 public class BuilderCache
1093 {
72b7c289
TB
1094 private final List<IPRange> mAddresses = new ArrayList<>();
1095 private final List<IPRange> mRoutesIPv4 = new ArrayList<>();
1096 private final List<IPRange> mRoutesIPv6 = new ArrayList<>();
4471a934
TB
1097 private final IPRangeSet mIncludedSubnetsv4 = new IPRangeSet();
1098 private final IPRangeSet mIncludedSubnetsv6 = new IPRangeSet();
72b7c289 1099 private final IPRangeSet mExcludedSubnets;
acc8948f 1100 private final int mSplitTunneling;
f2e7156d 1101 private final SelectedAppsHandling mAppHandling;
800f881a 1102 private final SortedSet<String> mSelectedApps;
dd5de792 1103 private final List<InetAddress> mDnsServers = new ArrayList<>();
36aab70a 1104 private int mMtu;
dd5de792 1105 private boolean mIPv4Seen, mIPv6Seen, mDnsServersConfigured;
acc8948f 1106
f2e7156d 1107 public BuilderCache(VpnProfile profile)
acc8948f 1108 {
f2e7156d 1109 IPRangeSet included = IPRangeSet.fromString(profile.getIncludedSubnets());
4471a934
TB
1110 for (IPRange range : included)
1111 {
1112 if (range.getFrom() instanceof Inet4Address)
1113 {
1114 mIncludedSubnetsv4.add(range);
1115 }
1116 else if (range.getFrom() instanceof Inet6Address)
1117 {
1118 mIncludedSubnetsv6.add(range);
1119 }
1120 }
f2e7156d
TB
1121 mExcludedSubnets = IPRangeSet.fromString(profile.getExcludedSubnets());
1122 Integer splitTunneling = profile.getSplitTunneling();
acc8948f 1123 mSplitTunneling = splitTunneling != null ? splitTunneling : 0;
99cc2d82 1124 SelectedAppsHandling appHandling = profile.getSelectedAppsHandling();
800f881a 1125 mSelectedApps = profile.getSelectedAppsSet();
99cc2d82
TB
1126 /* exclude our own app, otherwise the fetcher is blocked */
1127 switch (appHandling)
1128 {
1129 case SELECTED_APPS_DISABLE:
1130 appHandling = SelectedAppsHandling.SELECTED_APPS_EXCLUDE;
1131 mSelectedApps.clear();
1132 /* fall-through */
1133 case SELECTED_APPS_EXCLUDE:
1134 mSelectedApps.add(getPackageName());
1135 break;
1136 case SELECTED_APPS_ONLY:
1137 mSelectedApps.remove(getPackageName());
1138 break;
1139 }
1140 mAppHandling = appHandling;
fe1f1432 1141
dd5de792
TB
1142 if (profile.getDnsServers() != null)
1143 {
1144 for (String server : profile.getDnsServers().split("\\s+"))
1145 {
1146 try
1147 {
2ef473be 1148 mDnsServers.add(Utils.parseInetAddress(server));
dd5de792
TB
1149 recordAddressFamily(server);
1150 mDnsServersConfigured = true;
1151 }
1152 catch (UnknownHostException e)
1153 {
1154 e.printStackTrace();
1155 }
1156 }
1157 }
1158
fe1f1432
TB
1159 /* set a default MTU, will be set by the daemon for regular interfaces */
1160 Integer mtu = profile.getMTU();
1161 mMtu = mtu == null ? Constants.MTU_MAX : mtu;
acc8948f 1162 }
36aab70a
TB
1163
1164 public void addAddress(String address, int prefixLength)
1165 {
72b7c289
TB
1166 try
1167 {
1168 mAddresses.add(new IPRange(address, prefixLength));
1169 recordAddressFamily(address);
1170 }
1171 catch (UnknownHostException ex)
1172 {
1173 ex.printStackTrace();
1174 }
36aab70a
TB
1175 }
1176
dd5de792
TB
1177 public void addDnsServer(String address)
1178 {
1179 /* ignore received DNS servers if any were configured */
1180 if (mDnsServersConfigured)
1181 {
1182 return;
1183 }
1184
1185 try
1186 {
2ef473be 1187 mDnsServers.add(Utils.parseInetAddress(address));
dd5de792
TB
1188 recordAddressFamily(address);
1189 }
1190 catch (UnknownHostException e)
1191 {
1192 e.printStackTrace();
1193 }
1194 }
1195
36aab70a
TB
1196 public void addRoute(String address, int prefixLength)
1197 {
acc8948f
TB
1198 try
1199 {
1200 if (isIPv6(address))
1201 {
72b7c289 1202 mRoutesIPv6.add(new IPRange(address, prefixLength));
acc8948f
TB
1203 }
1204 else
1205 {
72b7c289 1206 mRoutesIPv4.add(new IPRange(address, prefixLength));
acc8948f
TB
1207 }
1208 }
1209 catch (UnknownHostException ex)
1210 {
1211 ex.printStackTrace();
1212 }
36aab70a
TB
1213 }
1214
1215 public void setMtu(int mtu)
1216 {
1217 mMtu = mtu;
1218 }
1219
acc8948f
TB
1220 public void recordAddressFamily(String address)
1221 {
1222 try
1223 {
1224 if (isIPv6(address))
1225 {
1226 mIPv6Seen = true;
1227 }
1228 else
1229 {
1230 mIPv4Seen = true;
1231 }
1232 }
1233 catch (UnknownHostException ex)
1234 {
1235 ex.printStackTrace();
1236 }
1237 }
1238
1239 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
36aab70a
TB
1240 public void applyData(VpnService.Builder builder)
1241 {
72b7c289 1242 for (IPRange address : mAddresses)
36aab70a 1243 {
72b7c289 1244 builder.addAddress(address.getFrom(), address.getPrefix());
36aab70a 1245 }
dd5de792
TB
1246 for (InetAddress server : mDnsServers)
1247 {
1248 builder.addDnsServer(server);
1249 }
acc8948f
TB
1250 /* add routes depending on whether split tunneling is allowed or not,
1251 * that is, whether we have to handle and block non-VPN traffic */
1252 if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) == 0)
1253 {
1254 if (mIPv4Seen)
72b7c289
TB
1255 { /* split tunneling is used depending on the routes and configuration */
1256 IPRangeSet ranges = new IPRangeSet();
4471a934
TB
1257 if (mIncludedSubnetsv4.size() > 0)
1258 {
1259 ranges.add(mIncludedSubnetsv4);
1260 }
1261 else
1262 {
1263 ranges.addAll(mRoutesIPv4);
1264 }
72b7c289
TB
1265 ranges.remove(mExcludedSubnets);
1266 for (IPRange subnet : ranges.subnets())
acc8948f 1267 {
66b7a088
TB
1268 try
1269 {
1270 builder.addRoute(subnet.getFrom(), subnet.getPrefix());
1271 }
1272 catch (IllegalArgumentException e)
1273 { /* some Android versions don't seem to like multicast addresses here,
1274 * ignore it for now */
1275 if (!subnet.getFrom().isMulticastAddress())
1276 {
1277 throw e;
1278 }
1279 }
acc8948f
TB
1280 }
1281 }
a3e895b4 1282 else
acc8948f
TB
1283 { /* allow traffic that would otherwise be blocked to bypass the VPN */
1284 builder.allowFamily(OsConstants.AF_INET);
1285 }
1286 }
1287 else if (mIPv4Seen)
1288 { /* only needed if we've seen any addresses. otherwise, traffic
1289 * is blocked by default (we also install no routes in that case) */
1290 builder.addRoute("0.0.0.0", 0);
1291 }
1292 /* same thing for IPv6 */
1293 if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) == 0)
1294 {
1295 if (mIPv6Seen)
1296 {
72b7c289 1297 IPRangeSet ranges = new IPRangeSet();
4471a934
TB
1298 if (mIncludedSubnetsv6.size() > 0)
1299 {
1300 ranges.add(mIncludedSubnetsv6);
1301 }
1302 else
1303 {
1304 ranges.addAll(mRoutesIPv6);
1305 }
72b7c289
TB
1306 ranges.remove(mExcludedSubnets);
1307 for (IPRange subnet : ranges.subnets())
acc8948f 1308 {
66b7a088
TB
1309 try
1310 {
1311 builder.addRoute(subnet.getFrom(), subnet.getPrefix());
1312 }
1313 catch (IllegalArgumentException e)
1314 {
1315 if (!subnet.getFrom().isMulticastAddress())
1316 {
1317 throw e;
1318 }
1319 }
acc8948f
TB
1320 }
1321 }
a3e895b4 1322 else
acc8948f
TB
1323 {
1324 builder.allowFamily(OsConstants.AF_INET6);
1325 }
1326 }
1327 else if (mIPv6Seen)
36aab70a 1328 {
acc8948f 1329 builder.addRoute("::", 0);
36aab70a 1330 }
f2e7156d 1331 /* apply selected applications */
a3e895b4 1332 if (mSelectedApps.size() > 0)
f2e7156d
TB
1333 {
1334 switch (mAppHandling)
1335 {
1336 case SELECTED_APPS_EXCLUDE:
1337 for (String app : mSelectedApps)
1338 {
1339 try
1340 {
1341 builder.addDisallowedApplication(app);
1342 }
1343 catch (PackageManager.NameNotFoundException e)
1344 {
1345 // possible if not configured via GUI or app was uninstalled
1346 }
1347 }
1348 break;
1349 case SELECTED_APPS_ONLY:
1350 for (String app : mSelectedApps)
1351 {
1352 try
1353 {
1354 builder.addAllowedApplication(app);
1355 }
1356 catch (PackageManager.NameNotFoundException e)
1357 {
1358 // possible if not configured via GUI or app was uninstalled
1359 }
1360 }
1361 break;
1362 default:
1363 break;
1364 }
1365 }
36aab70a
TB
1366 builder.setMtu(mMtu);
1367 }
1368
acc8948f
TB
1369 private boolean isIPv6(String address) throws UnknownHostException
1370 {
2ef473be 1371 InetAddress addr = Utils.parseInetAddress(address);
acc8948f
TB
1372 if (addr instanceof Inet4Address)
1373 {
1374 return false;
1375 }
a3e895b4 1376 return addr instanceof Inet6Address;
acc8948f 1377 }
5215d512
TB
1378 }
1379
c5ba3817
TB
1380 /**
1381 * Function called via JNI to determine information about the Android version.
1382 */
1383 private static String getAndroidVersion()
1384 {
a63b0f99
TB
1385 String version = "Android " + Build.VERSION.RELEASE + " - " + Build.DISPLAY;
1386 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
1387 {
1388 version += "/" + Build.VERSION.SECURITY_PATCH;
1389 }
1390 return version;
c5ba3817
TB
1391 }
1392
1393 /**
1394 * Function called via JNI to determine information about the device.
1395 */
1396 private static String getDeviceString()
1397 {
1398 return Build.MODEL + " - " + Build.BRAND + "/" + Build.PRODUCT + "/" + Build.MANUFACTURER;
1399 }
4a208143 1400}