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