From 2d9931bd658ee50fbd2f959b80e90a9fd9fefa1d Mon Sep 17 00:00:00 2001 From: fil Date: Fri, 17 Oct 2025 00:50:18 +0700 Subject: [PATCH] Added to experimental settings obfuscation method --- .../vpn/{utils => enums}/NetworkType.java | 2 +- .../fptn/vpn/enums/ObfuscationMethodEnum.java | 27 +++++++++++ .../vpn/services/CustomVpnConnection.java | 9 ++-- .../fptn/vpn/services/CustomVpnService.java | 7 ++- .../websocket/NativeWebSocketClientImpl.java | 10 +++- .../websocket/WebSocketClientWrapper.java | 9 +++- .../java/org/fptn/vpn/utils/NetworkUtils.java | 2 + .../org/fptn/vpn/utils/SharedPrefUtils.java | 16 +++++-- .../org/fptn/vpn/views/SettingsActivity.java | 20 ++++++-- .../adapter/ObfuscationMethodAdapter.java | 47 +++++++++++++++++++ .../layout/experimental_settings_dialog.xml | 43 +++++++++++++++++ .../obfuscation_method_spinner_item.xml | 9 ++++ app/src/main/res/values-ru-rRU/strings.xml | 2 + app/src/main/res/values/strings.xml | 3 ++ .../org/fptn/vpn/core/common/Constants.kt | 1 + 15 files changed, 190 insertions(+), 17 deletions(-) rename app/src/main/java/org/fptn/vpn/{utils => enums}/NetworkType.java (70%) create mode 100644 app/src/main/java/org/fptn/vpn/enums/ObfuscationMethodEnum.java create mode 100644 app/src/main/java/org/fptn/vpn/views/adapter/ObfuscationMethodAdapter.java create mode 100644 app/src/main/res/layout/obfuscation_method_spinner_item.xml diff --git a/app/src/main/java/org/fptn/vpn/utils/NetworkType.java b/app/src/main/java/org/fptn/vpn/enums/NetworkType.java similarity index 70% rename from app/src/main/java/org/fptn/vpn/utils/NetworkType.java rename to app/src/main/java/org/fptn/vpn/enums/NetworkType.java index d1448160..8df81340 100644 --- a/app/src/main/java/org/fptn/vpn/utils/NetworkType.java +++ b/app/src/main/java/org/fptn/vpn/enums/NetworkType.java @@ -1,4 +1,4 @@ -package org.fptn.vpn.utils; +package org.fptn.vpn.enums; public enum NetworkType { WIFI, CELLULAR, ETHERNET, UNKNOWN diff --git a/app/src/main/java/org/fptn/vpn/enums/ObfuscationMethodEnum.java b/app/src/main/java/org/fptn/vpn/enums/ObfuscationMethodEnum.java new file mode 100644 index 00000000..8f7ab866 --- /dev/null +++ b/app/src/main/java/org/fptn/vpn/enums/ObfuscationMethodEnum.java @@ -0,0 +1,27 @@ +package org.fptn.vpn.enums; + +import androidx.annotation.NonNull; + +import lombok.Getter; + +public enum ObfuscationMethodEnum { + NONE("None", 0), + TLS_APP_DATA("TLS App Data", 1), + TLS_APP_DATA_BASE_64("TLS App Data + base64", 2); + + final String description; + + @Getter + final int id; + + ObfuscationMethodEnum(String description, int id) { + this.description = description; + this.id = id; + } + + @NonNull + @Override + public String toString() { + return description; + } +} diff --git a/app/src/main/java/org/fptn/vpn/services/CustomVpnConnection.java b/app/src/main/java/org/fptn/vpn/services/CustomVpnConnection.java index 6750af28..8b1d656a 100644 --- a/app/src/main/java/org/fptn/vpn/services/CustomVpnConnection.java +++ b/app/src/main/java/org/fptn/vpn/services/CustomVpnConnection.java @@ -18,11 +18,12 @@ import org.fptn.vpn.database.model.FptnServerDto; import org.fptn.vpn.enums.ConnectionState; import org.fptn.vpn.enums.HandlerMessageTypes; +import org.fptn.vpn.enums.ObfuscationMethodEnum; import org.fptn.vpn.services.websocket.WebSocketAlreadyShutdownException; import org.fptn.vpn.services.websocket.WebSocketClientWrapper; import org.fptn.vpn.utils.DataRateCalculator; import org.fptn.vpn.utils.IPUtils; -import org.fptn.vpn.utils.NetworkType; +import org.fptn.vpn.enums.NetworkType; import org.fptn.vpn.vpnclient.exception.ErrorCode; import org.fptn.vpn.vpnclient.exception.PVNClientException; @@ -101,7 +102,8 @@ public CustomVpnConnection(final CustomVpnService service, final String currentIPAddress, final NetworkType currentNetworkType, final int maxReconnectCount, - int delayBetweenAttempts) throws PVNClientException { + final int delayBetweenAttempts, + final ObfuscationMethodEnum obfuscationMethod) throws PVNClientException { this.service = service; this.connectionId = connectionId; this.fptnServerDto = fptnServerDto; @@ -112,7 +114,8 @@ public CustomVpnConnection(final CustomVpnService service, sniHostName, this::onConnectionOpen, this::onMessageReceived, - this::onConnectionFailure + this::onConnectionFailure, + obfuscationMethod ); this.maxReconnectCount = maxReconnectCount; this.delayBetweenAttempts = delayBetweenAttempts; diff --git a/app/src/main/java/org/fptn/vpn/services/CustomVpnService.java b/app/src/main/java/org/fptn/vpn/services/CustomVpnService.java index 58a629ed..873fd747 100644 --- a/app/src/main/java/org/fptn/vpn/services/CustomVpnService.java +++ b/app/src/main/java/org/fptn/vpn/services/CustomVpnService.java @@ -36,9 +36,10 @@ import org.fptn.vpn.database.model.FptnServerDto; import org.fptn.vpn.enums.ConnectionState; import org.fptn.vpn.enums.HandlerMessageTypes; +import org.fptn.vpn.enums.NetworkType; +import org.fptn.vpn.enums.ObfuscationMethodEnum; import org.fptn.vpn.repository.FptnServerRepository; import org.fptn.vpn.services.tile.FptnTileService; -import org.fptn.vpn.utils.NetworkType; import org.fptn.vpn.utils.NetworkUtils; import org.fptn.vpn.utils.NotificationUtils; import org.fptn.vpn.utils.SharedPrefUtils; @@ -455,6 +456,7 @@ private void connect(FptnServerDto fptnServerDto, String sniHostname) { int delayBetweenAttempts = SharedPrefUtils.getDelayBetweenReconnect(this); try { + ObfuscationMethodEnum obfuscationMethod = SharedPrefUtils.getObfuscationMethod(this); CustomVpnConnection connection = new CustomVpnConnection( this, nextConnectionId.getAndIncrement(), @@ -463,7 +465,8 @@ private void connect(FptnServerDto fptnServerDto, String sniHostname) { currentIPAddress, networkType, maxReconnectCount, - delayBetweenAttempts); + delayBetweenAttempts, + obfuscationMethod); connection.setConfigureVpnIntent(launchMainActivityPendingIntent); connection.start(); diff --git a/app/src/main/java/org/fptn/vpn/services/websocket/NativeWebSocketClientImpl.java b/app/src/main/java/org/fptn/vpn/services/websocket/NativeWebSocketClientImpl.java index f4540fa2..4b14d5d1 100644 --- a/app/src/main/java/org/fptn/vpn/services/websocket/NativeWebSocketClientImpl.java +++ b/app/src/main/java/org/fptn/vpn/services/websocket/NativeWebSocketClientImpl.java @@ -2,6 +2,7 @@ import android.util.Log; +import org.fptn.vpn.enums.ObfuscationMethodEnum; import org.fptn.vpn.services.websocket.callback.OnFailureCallback; import org.fptn.vpn.services.websocket.callback.OnMessageReceivedCallback; import org.fptn.vpn.services.websocket.callback.OnOpenCallback; @@ -24,6 +25,7 @@ public class NativeWebSocketClientImpl { private final OnOpenCallback onOpenCallback; private final OnMessageReceivedCallback onMessageReceivedCallback; private final OnFailureCallback onFailureCallback; + private final ObfuscationMethodEnum obfuscationMethod; private long nativeHandle; @@ -36,11 +38,17 @@ public NativeWebSocketClientImpl( String md5ServerFingerprint, OnOpenCallback onOpenCallback, OnMessageReceivedCallback onMessageReceivedCallback, - OnFailureCallback onFailureCallback) throws PVNClientException { + OnFailureCallback onFailureCallback, + ObfuscationMethodEnum obfuscationMethod) throws PVNClientException { this.onOpenCallback = onOpenCallback; this.onMessageReceivedCallback = onMessageReceivedCallback; this.onFailureCallback = onFailureCallback; + this.obfuscationMethod = obfuscationMethod; + + // TODO: WHAT TO DO WITH OBFUSCATION METHOD? + // USE THIS + String obfuscation = obfuscationMethod.name(); // NONE, TLS_APP_DATA, TLS_APP_DATA_BASE_64 this.nativeHandle = nativeCreate( host, port, diff --git a/app/src/main/java/org/fptn/vpn/services/websocket/WebSocketClientWrapper.java b/app/src/main/java/org/fptn/vpn/services/websocket/WebSocketClientWrapper.java index aefc1fb7..05a5562d 100644 --- a/app/src/main/java/org/fptn/vpn/services/websocket/WebSocketClientWrapper.java +++ b/app/src/main/java/org/fptn/vpn/services/websocket/WebSocketClientWrapper.java @@ -3,6 +3,7 @@ import android.util.Log; import org.fptn.vpn.database.model.FptnServerDto; +import org.fptn.vpn.enums.ObfuscationMethodEnum; import org.fptn.vpn.services.websocket.callback.OnFailureCallback; import org.fptn.vpn.services.websocket.callback.OnMessageReceivedCallback; import org.fptn.vpn.services.websocket.callback.OnOpenCallback; @@ -24,6 +25,7 @@ public class WebSocketClientWrapper { private final OnMessageReceivedCallback onMessageReceivedCallback; private final OnFailureCallback onFailureCallback; private final NativeHttpsClientImpl nativeHttpsClient; + private final ObfuscationMethodEnum obfuscationMethod; private NativeWebSocketClientImpl nativeWebSocketClient; @@ -35,7 +37,8 @@ public WebSocketClientWrapper(FptnServerDto fptnServerDto, String sniHostName, OnOpenCallback onOpenCallback, OnMessageReceivedCallback onMessageReceivedCallback, - OnFailureCallback onFailureCallback) { + OnFailureCallback onFailureCallback, + ObfuscationMethodEnum obfuscationMethod) { this.fptnServerDto = fptnServerDto; this.tunAddress = tunAddress; this.sniHostName = sniHostName; @@ -43,6 +46,7 @@ public WebSocketClientWrapper(FptnServerDto fptnServerDto, this.onMessageReceivedCallback = onMessageReceivedCallback; this.onFailureCallback = onFailureCallback; this.nativeHttpsClient = new NativeHttpsClientImpl(fptnServerDto.host, fptnServerDto.port, this.sniHostName, fptnServerDto.md5ServerFingerprint); + this.obfuscationMethod = obfuscationMethod; } public synchronized void startWebSocket() throws PVNClientException, WebSocketAlreadyShutdownException { @@ -61,7 +65,8 @@ public synchronized void startWebSocket() throws PVNClientException, WebSocketAl fptnServerDto.md5ServerFingerprint, onOpenCallback, onMessageReceivedCallback, - onFailureCallback + onFailureCallback, + obfuscationMethod ); Log.d(getTag(), "startWebSocket() nativeWebSocketClient.start() Thread.id: " + Thread.currentThread().getId()); nativeWebSocketClient.start(); diff --git a/app/src/main/java/org/fptn/vpn/utils/NetworkUtils.java b/app/src/main/java/org/fptn/vpn/utils/NetworkUtils.java index 56694828..834c62d7 100644 --- a/app/src/main/java/org/fptn/vpn/utils/NetworkUtils.java +++ b/app/src/main/java/org/fptn/vpn/utils/NetworkUtils.java @@ -5,6 +5,8 @@ import android.net.NetworkRequest; import android.util.Log; +import org.fptn.vpn.enums.NetworkType; + import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; diff --git a/app/src/main/java/org/fptn/vpn/utils/SharedPrefUtils.java b/app/src/main/java/org/fptn/vpn/utils/SharedPrefUtils.java index 0aa32c0f..ab7f0911 100644 --- a/app/src/main/java/org/fptn/vpn/utils/SharedPrefUtils.java +++ b/app/src/main/java/org/fptn/vpn/utils/SharedPrefUtils.java @@ -2,10 +2,10 @@ import android.content.Context; import android.content.SharedPreferences; -import android.util.Log; import org.fptn.vpn.R; import org.fptn.vpn.core.common.Constants; +import org.fptn.vpn.enums.ObfuscationMethodEnum; public class SharedPrefUtils { private static final String TAG = SharedPrefUtils.class.getSimpleName(); @@ -87,8 +87,6 @@ public static int getReconnectAttemptsCount(Context context) { } public static void saveReconnectAttemptsCount(Context context, int count) { - Log.d(TAG, "saveReconnectAttemptsCount: " + count); - SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE); sharedPreferences.edit().putInt(Constants.RECONNECT_ATTEMPTS_COUNT_SHARED_PREF_KEY, count).apply(); } @@ -100,8 +98,18 @@ public static int getDelayBetweenReconnect(Context context) { } public static void saveDelayBetweenReconnect(Context context, int delayInSeconds) { - Log.d(TAG, "saveDelayBetweenReconnect: " + delayInSeconds); SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE); sharedPreferences.edit().putInt(Constants.RECONNECT_DELAY_BETWEEN_SHARED_PREF_KEY, delayInSeconds).apply(); } + + + public static ObfuscationMethodEnum getObfuscationMethod(Context context) { + SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE); + return ObfuscationMethodEnum.valueOf(sharedPreferences.getString(Constants.OBFUSCATION_METHOD_SHARED_PREF_KEY, ObfuscationMethodEnum.NONE.name())); + } + + public static void saveObfuscationMethod(Context context, ObfuscationMethodEnum obfuscationMethodEnum) { + SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE); + sharedPreferences.edit().putString(Constants.OBFUSCATION_METHOD_SHARED_PREF_KEY, obfuscationMethodEnum.name()).apply(); + } } diff --git a/app/src/main/java/org/fptn/vpn/views/SettingsActivity.java b/app/src/main/java/org/fptn/vpn/views/SettingsActivity.java index 3067ee3c..66030a05 100644 --- a/app/src/main/java/org/fptn/vpn/views/SettingsActivity.java +++ b/app/src/main/java/org/fptn/vpn/views/SettingsActivity.java @@ -22,6 +22,7 @@ import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SeekBar; +import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; @@ -32,18 +33,19 @@ import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModelProvider; +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.textfield.TextInputEditText; + import org.fptn.vpn.R; +import org.fptn.vpn.enums.ObfuscationMethodEnum; import org.fptn.vpn.services.tile.FptnTileService; import org.fptn.vpn.utils.PermissionsUtils; import org.fptn.vpn.utils.SharedPrefUtils; import org.fptn.vpn.viewmodel.FptnServerViewModel; import org.fptn.vpn.views.adapter.FptnServerAdapter; - -import com.google.android.material.bottomnavigation.BottomNavigationView; -import com.google.android.material.textfield.TextInputEditText; +import org.fptn.vpn.views.adapter.ObfuscationMethodAdapter; import java.util.Optional; -import java.util.concurrent.Executors; import lombok.Getter; @@ -401,6 +403,13 @@ public void onStopTrackingTouch(SeekBar seekBar) { seekBarDelayBetween.setProgress(0); seekBarDelayBetween.setProgress(delayBetweenReconnect - 1); + /* Obfuscation method spinner*/ + Spinner obfuscationMethodSpinner = dialogView.findViewById(R.id.obfuscation_method_spinner); + obfuscationMethodSpinner.setAdapter(new ObfuscationMethodAdapter()); + + ObfuscationMethodEnum obfuscationMethod = SharedPrefUtils.getObfuscationMethod(this); + obfuscationMethodSpinner.setSelection(obfuscationMethod.getId()); + builder.setPositiveButton(getString(R.string.save_button), (dialog, which) -> { Log.d(TAG, "experimentalFeaturesDialog: save"); SharedPrefUtils.saveReconnectOnChangeNetworkTypeEnabled(this, switchNetworkType.isChecked()); @@ -415,6 +424,9 @@ public void onStopTrackingTouch(SeekBar seekBar) { int delayBetweenProgress = seekBarDelayBetween.getProgress(); SharedPrefUtils.saveDelayBetweenReconnect(this, delayBetweenProgress + 1); + + ObfuscationMethodEnum selectedObfuscationMethod = (ObfuscationMethodEnum) obfuscationMethodSpinner.getSelectedItem(); + SharedPrefUtils.saveObfuscationMethod(this, selectedObfuscationMethod); }); builder.setNegativeButton(getString(R.string.cancel_button), (dialog, which) -> { Log.d(TAG, "experimentalFeaturesDialog: cancel"); diff --git a/app/src/main/java/org/fptn/vpn/views/adapter/ObfuscationMethodAdapter.java b/app/src/main/java/org/fptn/vpn/views/adapter/ObfuscationMethodAdapter.java new file mode 100644 index 00000000..66c74303 --- /dev/null +++ b/app/src/main/java/org/fptn/vpn/views/adapter/ObfuscationMethodAdapter.java @@ -0,0 +1,47 @@ +package org.fptn.vpn.views.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import org.fptn.vpn.R; +import org.fptn.vpn.enums.ObfuscationMethodEnum; + +import lombok.Getter; + +@Getter +public class ObfuscationMethodAdapter extends BaseAdapter { + private final ObfuscationMethodEnum[] values = ObfuscationMethodEnum.values(); + + public ObfuscationMethodAdapter() { + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return values.length; + } + + @Override + public Object getItem(int position) { + return values[position]; + } + + @Override + public long getItemId(int position) { + return values[position].getId(); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + if (view == null) { + view = LayoutInflater.from(parent.getContext()).inflate(R.layout.obfuscation_method_spinner_item, parent, false); + } + + TextView label = view.findViewById(R.id.obfuscation_method_spinner_item_label); + label.setText(values[position].toString()); + return view; + } +} diff --git a/app/src/main/res/layout/experimental_settings_dialog.xml b/app/src/main/res/layout/experimental_settings_dialog.xml index f6971bcc..9ab45dc1 100644 --- a/app/src/main/res/layout/experimental_settings_dialog.xml +++ b/app/src/main/res/layout/experimental_settings_dialog.xml @@ -156,6 +156,49 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index 1b0e09b1..1f4e1b80 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -150,4 +150,6 @@ https://play.google.com/store/apps/details?id=org.fptn.vpn Плитка быстрых настроек была добавлена ранее Плитка быстрых настроек добавлена Плитка быстрых настроек не добавлена! + Настроки протокола: + Маскировка: \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5de2aded..2352cc5d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -174,4 +174,7 @@ https://play.google.com/store/apps/details?id=org.fptn.vpn Tile already added Tile added successfully Tile addition failed! + + Protocol settings: + Obfuscation: diff --git a/core/common/src/main/kotlin/org/fptn/vpn/core/common/Constants.kt b/core/common/src/main/kotlin/org/fptn/vpn/core/common/Constants.kt index 39b0d786..6dabc4f5 100644 --- a/core/common/src/main/kotlin/org/fptn/vpn/core/common/Constants.kt +++ b/core/common/src/main/kotlin/org/fptn/vpn/core/common/Constants.kt @@ -22,4 +22,5 @@ object Constants { "RECONNECT_ON_CHANGE_NETWORK_TYPE_ENABLED" const val RECONNECT_ATTEMPTS_COUNT_SHARED_PREF_KEY: String = "RECONNECT_ATTEMPTS_COUNT_SHARED_PREF_KEY" const val RECONNECT_DELAY_BETWEEN_SHARED_PREF_KEY: String = "RECONNECT_DELAY_BETWEEN_SHARED_PREF_KEY" + const val OBFUSCATION_METHOD_SHARED_PREF_KEY: String = "TLS_OBFUSCATION_METHOD_SHARED_PREF_KEY" }