├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetDropDown.xml ├── google-java-format.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── mokhtarabadi │ │ └── tun2socks │ │ └── sample │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── bypass_private_ips.txt │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── native-lib.cpp │ │ └── prebuilt │ │ │ ├── include │ │ │ └── tun2socks │ │ │ │ └── tun2socks.h │ │ │ └── lib │ │ │ ├── arm64-v8a │ │ │ └── libtun2socks.a │ │ │ ├── armeabi-v7a │ │ │ └── libtun2socks.a │ │ │ ├── x86 │ │ │ └── libtun2socks.a │ │ │ └── x86_64 │ │ │ └── libtun2socks.a │ ├── java │ │ └── com │ │ │ └── mokhtarabadi │ │ │ └── tun2socks │ │ │ └── sample │ │ │ ├── ExcludedAppsActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MainApp.java │ │ │ ├── MainNative.java │ │ │ ├── MainService.java │ │ │ ├── PreferenceHelper.java │ │ │ └── SettingsActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_baseline_stop_24.xml │ │ ├── ic_baseline_vpn_lock_24.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_excluded_apps.xml │ │ ├── activity_main.xml │ │ ├── activity_settings.xml │ │ └── layout_app_item.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── mokhtarabadi │ └── tun2socks │ └── sample │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Universal Android Tun2Socks -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/google-java-format.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Mohammad Reza Mokhtarabadi 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # universal-android-tun2socks 2 | An universal (badvpn)tun2socks for Android, with easy usage, just need call Java APIs... 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 30 7 | 8 | buildFeatures { 9 | viewBinding true 10 | } 11 | 12 | defaultConfig { 13 | applicationId "com.mokhtarabadi.tun2socks.sample" 14 | minSdk 18 15 | targetSdk 30 16 | versionCode 1 17 | versionName "1.0" 18 | 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | 21 | externalNativeBuild { 22 | cmake { 23 | cppFlags "-std=c++11" 24 | arguments "-DANDROID_STL=c++_static" 25 | } 26 | } 27 | } 28 | 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | 36 | externalNativeBuild { 37 | cmake { 38 | path "src/main/cpp/CMakeLists.txt" 39 | version "3.10.2" 40 | } 41 | } 42 | 43 | compileOptions { 44 | sourceCompatibility JavaVersion.VERSION_1_8 45 | targetCompatibility JavaVersion.VERSION_1_8 46 | } 47 | } 48 | 49 | dependencies { 50 | implementation 'com.getkeepsafe.relinker:relinker:1.4.4' 51 | implementation 'com.frybits.harmony:harmony:1.1.9' 52 | 53 | implementation 'androidx.appcompat:appcompat:1.3.1' 54 | implementation 'com.google.android.material:material:1.4.0' 55 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0' 56 | implementation 'androidx.recyclerview:recyclerview:1.2.1' 57 | 58 | testImplementation 'junit:junit:4.+' 59 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 60 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 61 | } 62 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mokhtarabadi/tun2socks/sample/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.mokhtarabadi.tun2socks.sample", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/assets/bypass_private_ips.txt: -------------------------------------------------------------------------------- 1 | 0.0.0.0/5 2 | 8.0.0.0/7 3 | 11.0.0.0/8 4 | 12.0.0.0/6 5 | 16.0.0.0/4 6 | 32.0.0.0/3 7 | 64.0.0.0/2 8 | 128.0.0.0/3 9 | 160.0.0.0/5 10 | 168.0.0.0/6 11 | 172.0.0.0/12 12 | 172.32.0.0/11 13 | 172.64.0.0/10 14 | 172.128.0.0/9 15 | 173.0.0.0/8 16 | 174.0.0.0/7 17 | 176.0.0.0/4 18 | 192.0.0.0/9 19 | 192.128.0.0/11 20 | 192.160.0.0/13 21 | 192.169.0.0/16 22 | 192.170.0.0/15 23 | 192.172.0.0/14 24 | 192.176.0.0/12 25 | 192.192.0.0/10 26 | 193.0.0.0/8 27 | 194.0.0.0/7 28 | 196.0.0.0/6 29 | 200.0.0.0/5 30 | 208.0.0.0/4 31 | 224.0.0.0/3 -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | project("native-lib") 3 | 4 | # log-lib 5 | find_library(log-lib 6 | log) 7 | 8 | # tun2socks-bridge 9 | add_library(native-lib 10 | SHARED 11 | native-lib.cpp) 12 | 13 | target_include_directories(native-lib PRIVATE 14 | ${CMAKE_SOURCE_DIR}/prebuilt/include) 15 | 16 | target_link_libraries(native-lib 17 | ${CMAKE_SOURCE_DIR}/prebuilt/lib/${ANDROID_ABI}/libtun2socks.a 18 | ${log-lib}) -------------------------------------------------------------------------------- /app/src/main/cpp/README.md: -------------------------------------------------------------------------------- 1 | ## Notice 2 | `prebuilt` directory contains was copied from [this](https://github.com/mokhtarabadi/badvpn) repository. 3 | -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // Start threads to redirect stdout and stderr to logcat. 10 | int pipe_stdout[2]; 11 | int pipe_stderr[2]; 12 | pthread_t thread_stdout; 13 | pthread_t thread_stderr; 14 | const char *ADBTAG = "native-lib"; 15 | 16 | void *thread_stderr_func(void *) { 17 | ssize_t redirect_size; 18 | char buf[2048]; 19 | while ((redirect_size = read(pipe_stderr[0], buf, sizeof buf - 1)) > 0) { 20 | //__android_log will add a new line anyway. 21 | if (buf[redirect_size - 1] == '\n') { 22 | --redirect_size; 23 | } 24 | buf[redirect_size] = 0; 25 | __android_log_write(ANDROID_LOG_ERROR, ADBTAG, buf); 26 | } 27 | return 0; 28 | } 29 | 30 | void *thread_stdout_func(void *) { 31 | ssize_t redirect_size; 32 | char buf[2048]; 33 | while ((redirect_size = read(pipe_stdout[0], buf, sizeof buf - 1)) > 0) { 34 | //__android_log will add a new line anyway. 35 | if (buf[redirect_size - 1] == '\n') { 36 | --redirect_size; 37 | } 38 | buf[redirect_size] = 0; 39 | __android_log_write(ANDROID_LOG_INFO, ADBTAG, buf); 40 | } 41 | return 0; 42 | } 43 | 44 | int start_redirecting_stdout_stderr() { 45 | //set stdout as unbuffered. 46 | setvbuf(stdout, 0, _IONBF, 0); 47 | pipe(pipe_stdout); 48 | dup2(pipe_stdout[1], STDOUT_FILENO); 49 | 50 | //set stderr as unbuffered. 51 | setvbuf(stderr, 0, _IONBF, 0); 52 | pipe(pipe_stderr); 53 | dup2(pipe_stderr[1], STDERR_FILENO); 54 | 55 | if (pthread_create(&thread_stdout, 0, thread_stdout_func, 0) == -1) { 56 | return -1; 57 | } 58 | pthread_detach(thread_stdout); 59 | 60 | if (pthread_create(&thread_stderr, 0, thread_stderr_func, 0) == -1) { 61 | return -1; 62 | } 63 | pthread_detach(thread_stderr); 64 | 65 | return 0; 66 | } 67 | 68 | extern "C" 69 | JNIEXPORT jint JNICALL 70 | Java_com_mokhtarabadi_tun2socks_sample_MainNative_start_1tun2socks(JNIEnv *env, jclass clazz, 71 | jobjectArray args) { 72 | //argc 73 | jsize argument_count = env->GetArrayLength(args); 74 | 75 | //Compute byte size need for all arguments in contiguous memory. 76 | int c_arguments_size = 0; 77 | for (int i = 0; i < argument_count; i++) { 78 | c_arguments_size += strlen( 79 | env->GetStringUTFChars((jstring) env->GetObjectArrayElement(args, i), 0)); 80 | c_arguments_size++; // for '\0' 81 | } 82 | 83 | //Stores arguments in contiguous memory. 84 | char *args_buffer = (char *) calloc(c_arguments_size, sizeof(char)); 85 | 86 | //argv to pass into tun2socks. 87 | char *argv[argument_count]; 88 | 89 | //To iterate through the expected start position of each argument in args_buffer. 90 | char *current_args_position = args_buffer; 91 | 92 | //Populate the args_buffer and argv. 93 | for (int i = 0; i < argument_count; i++) { 94 | const char *current_argument = env->GetStringUTFChars( 95 | (jstring) env->GetObjectArrayElement(args, i), 0); 96 | 97 | //Copy current argument to its expected position in args_buffer 98 | strncpy(current_args_position, current_argument, strlen(current_argument)); 99 | 100 | //Save current argument start position in argv 101 | argv[i] = current_args_position; 102 | 103 | //Increment to the next argument's expected position. 104 | current_args_position += strlen(current_args_position) + 1; 105 | } 106 | 107 | //Start threads to show stdout and stderr in logcat. 108 | if (start_redirecting_stdout_stderr() == -1) { 109 | __android_log_write(ANDROID_LOG_ERROR, ADBTAG, 110 | "Couldn't start redirecting stdout and stderr to logcat."); 111 | } 112 | 113 | //Start tun2socks, with argc and argv. 114 | int result = tun2socks_start(argument_count, argv); 115 | free(args_buffer); 116 | 117 | return jint(result); 118 | } 119 | 120 | extern "C" 121 | JNIEXPORT void JNICALL 122 | Java_com_mokhtarabadi_tun2socks_sample_MainNative_stopTun2Socks(JNIEnv *env, jclass clazz) { 123 | tun2socks_terminate(); 124 | } 125 | 126 | extern "C" 127 | JNIEXPORT void JNICALL 128 | Java_com_mokhtarabadi_tun2socks_sample_MainNative_printTun2SocksHelp(JNIEnv *env, jclass clazz) { 129 | tun2socks_print_help("badvpn-tun2socks"); 130 | } 131 | 132 | extern "C" 133 | JNIEXPORT void JNICALL 134 | Java_com_mokhtarabadi_tun2socks_sample_MainNative_printTun2SocksVersion(JNIEnv *env, jclass clazz) { 135 | tun2socks_print_version(); 136 | } -------------------------------------------------------------------------------- /app/src/main/cpp/prebuilt/include/tun2socks/tun2socks.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Ambroz Bizjak 3 | * Contributions: 4 | * Making as Android library: Copyright (C) Mohammad Reza Mokhtarabadi 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the author nor the 14 | * names of its contributors may be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | // name of the program 30 | #define PROGRAM_NAME "tun2socks" 31 | 32 | // size of temporary buffer for passing data from the SOCKS server to TCP for sending 33 | #define CLIENT_SOCKS_RECV_BUF_SIZE 8192 34 | 35 | // maximum number of udpgw connections 36 | #define DEFAULT_UDPGW_MAX_CONNECTIONS 256 37 | 38 | // udpgw per-connection send buffer size, in number of packets 39 | #define DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE 8 40 | 41 | // udpgw reconnect time after connection fails 42 | #define UDPGW_RECONNECT_TIME 5000 43 | 44 | // udpgw keepalive sending interval 45 | #define UDPGW_KEEPALIVE_TIME 10000 46 | 47 | // option to override the destination addresses to give the SOCKS server 48 | //#define OVERRIDE_DEST_ADDR "10.111.0.2:2000" 49 | 50 | // Max number of buffered outgoing UDP packets for SOCKS5-UDP. It should be large 51 | // enough to prevent packet loss while the SOCKS UDP association is being set up. A slow 52 | // or far-away SOCKS server could require 300 ms to connect, and a chatty client (e.g. 53 | // STUN) could send a packet every 20 ms, so a default limit of 16 seems reasonable. 54 | #define SOCKS_UDP_SEND_BUFFER_PACKETS 16 55 | 56 | #ifdef __cplusplus 57 | extern "C" { 58 | #endif 59 | int tun2socks_start (int argc, char **argv); 60 | void tun2socks_terminate (void); 61 | void tun2socks_print_help (const char *name); 62 | void tun2socks_print_version (void); 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | -------------------------------------------------------------------------------- /app/src/main/cpp/prebuilt/lib/arm64-v8a/libtun2socks.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/cpp/prebuilt/lib/arm64-v8a/libtun2socks.a -------------------------------------------------------------------------------- /app/src/main/cpp/prebuilt/lib/armeabi-v7a/libtun2socks.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/cpp/prebuilt/lib/armeabi-v7a/libtun2socks.a -------------------------------------------------------------------------------- /app/src/main/cpp/prebuilt/lib/x86/libtun2socks.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/cpp/prebuilt/lib/x86/libtun2socks.a -------------------------------------------------------------------------------- /app/src/main/cpp/prebuilt/lib/x86_64/libtun2socks.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/cpp/prebuilt/lib/x86_64/libtun2socks.a -------------------------------------------------------------------------------- /app/src/main/java/com/mokhtarabadi/tun2socks/sample/ExcludedAppsActivity.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_EXCLUDED_APPS; 4 | 5 | import android.Manifest; 6 | import android.content.Context; 7 | import android.content.pm.PackageInfo; 8 | import android.content.pm.PackageManager; 9 | import android.os.Bundle; 10 | import android.text.Editable; 11 | import android.text.TextUtils; 12 | import android.text.TextWatcher; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.Filter; 17 | import android.widget.Filterable; 18 | 19 | import androidx.annotation.NonNull; 20 | import androidx.annotation.Nullable; 21 | import androidx.appcompat.app.AppCompatActivity; 22 | import androidx.recyclerview.widget.DiffUtil; 23 | import androidx.recyclerview.widget.LinearLayoutManager; 24 | import androidx.recyclerview.widget.RecyclerView; 25 | 26 | import com.google.android.material.textview.MaterialTextView; 27 | import com.mokhtarabadi.tun2socks.sample.databinding.ActivityExcludedAppsBinding; 28 | import com.mokhtarabadi.tun2socks.sample.databinding.LayoutAppItemBinding; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Collections; 32 | import java.util.HashSet; 33 | import java.util.List; 34 | import java.util.Locale; 35 | 36 | public class ExcludedAppsActivity extends AppCompatActivity { 37 | 38 | private ActivityExcludedAppsBinding binding; 39 | 40 | private List original; 41 | private List dataSet; 42 | 43 | private List selected; 44 | 45 | private AppAdapter adapter; 46 | 47 | @Override 48 | protected void onCreate(@Nullable Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | binding = ActivityExcludedAppsBinding.inflate(getLayoutInflater()); 51 | setContentView(binding.getRoot()); 52 | 53 | original = Collections.synchronizedList(new ArrayList<>()); 54 | dataSet = new ArrayList<>(); 55 | 56 | selected = new ArrayList<>(PreferenceHelper.getExcludedApps()); 57 | 58 | binding.recycler.setHasFixedSize(true); 59 | binding.recycler.setLayoutManager( 60 | new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); 61 | binding.recycler.setAdapter(adapter = new AppAdapter()); 62 | 63 | loadPackages(); 64 | } 65 | 66 | @Override 67 | protected void onDestroy() { 68 | PreferenceHelper.preferences 69 | .edit() 70 | .putStringSet(PREFERENCE_EXCLUDED_APPS, new HashSet<>(selected)) 71 | .apply(); 72 | 73 | super.onDestroy(); 74 | } 75 | 76 | private void loadPackages() { 77 | new Thread( 78 | () -> { 79 | for (PackageInfo installedPackage : 80 | getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS)) { 81 | if (installedPackage.packageName.equals("android") 82 | || installedPackage.packageName.equals(getPackageName())) { 83 | continue; 84 | } 85 | 86 | if (installedPackage.requestedPermissions != null) { 87 | for (String requestedPermission : installedPackage.requestedPermissions) { 88 | if (requestedPermission.equals(Manifest.permission.INTERNET)) { 89 | original.add(installedPackage); 90 | } 91 | } 92 | } 93 | } 94 | 95 | Collections.sort( 96 | original, 97 | (o1, o2) -> { 98 | int diff = 99 | Integer.compare( 100 | selected.indexOf(o2.packageName), selected.indexOf(o1.packageName)); 101 | 102 | if (diff != 0) { 103 | return diff; 104 | } 105 | 106 | diff = 107 | getPackageManager() 108 | .getApplicationLabel(o1.applicationInfo) 109 | .toString() 110 | .compareToIgnoreCase( 111 | getPackageManager() 112 | .getApplicationLabel(o2.applicationInfo) 113 | .toString()); 114 | 115 | return diff; 116 | }); 117 | 118 | runOnUiThread( 119 | () -> { 120 | binding.loading.setVisibility(View.GONE); 121 | binding.recycler.setVisibility(View.VISIBLE); 122 | binding.search.setVisibility(View.VISIBLE); 123 | 124 | dataSet.addAll(original); 125 | adapter.notifyItemRangeInserted(0, dataSet.size()); 126 | 127 | binding 128 | .search 129 | .getEditText() 130 | .addTextChangedListener( 131 | new TextWatcher() { 132 | @Override 133 | public void beforeTextChanged( 134 | CharSequence s, int start, int count, int after) {} 135 | 136 | @Override 137 | public void onTextChanged( 138 | CharSequence s, int start, int before, int count) { 139 | adapter.getFilter().filter(s); 140 | } 141 | 142 | @Override 143 | public void afterTextChanged(Editable s) {} 144 | }); 145 | }); 146 | }) 147 | .start(); 148 | } 149 | 150 | private static class AppDiffCallback extends DiffUtil.Callback { 151 | 152 | private final List oldDataSet; 153 | private final List newDataSet; 154 | 155 | public AppDiffCallback(List oldDataSet, List newDataSet) { 156 | this.oldDataSet = oldDataSet; 157 | this.newDataSet = newDataSet; 158 | } 159 | 160 | @Override 161 | public int getOldListSize() { 162 | return oldDataSet.size(); 163 | } 164 | 165 | @Override 166 | public int getNewListSize() { 167 | return newDataSet.size(); 168 | } 169 | 170 | @Override 171 | public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { 172 | return oldDataSet 173 | .get(oldItemPosition) 174 | .packageName 175 | .equals(newDataSet.get(newItemPosition).packageName); 176 | } 177 | 178 | @Override 179 | public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { 180 | return oldDataSet.get(oldItemPosition).equals(newDataSet.get(newItemPosition)); 181 | } 182 | } 183 | 184 | private class AppViewHolder extends RecyclerView.ViewHolder { 185 | 186 | private final Context context; 187 | private final LayoutAppItemBinding itemBinding; 188 | 189 | public AppViewHolder(@NonNull LayoutAppItemBinding itemBinding) { 190 | super(itemBinding.getRoot()); 191 | 192 | context = ExcludedAppsActivity.this; 193 | this.itemBinding = itemBinding; 194 | } 195 | 196 | private void bind(PackageInfo packageInfo) { 197 | itemBinding.label.setText( 198 | context.getPackageManager().getApplicationLabel(packageInfo.applicationInfo)); 199 | itemBinding.packageName.setText(packageInfo.packageName); 200 | itemBinding.excluded.setChecked(selected.contains(packageInfo.packageName)); 201 | itemBinding.icon.setImageDrawable( 202 | context.getPackageManager().getApplicationIcon(packageInfo.applicationInfo)); 203 | } 204 | } 205 | 206 | private class AppClickListener implements View.OnClickListener { 207 | @Override 208 | public void onClick(View v) { 209 | String packageName = 210 | ((MaterialTextView) v.findViewById(R.id.package_name)).getText().toString(); 211 | PackageInfo packageInfo = null; 212 | for (PackageInfo info : dataSet) { 213 | if (info.packageName.equals(packageName)) { 214 | packageInfo = info; 215 | break; 216 | } 217 | } 218 | int position = dataSet.indexOf(packageInfo); 219 | 220 | if (!selected.contains(packageName)) { 221 | selected.add(packageName); 222 | } else { 223 | selected.remove(packageName); 224 | } 225 | adapter.notifyItemChanged(position); 226 | } 227 | } 228 | 229 | private class AppAdapter extends RecyclerView.Adapter implements Filterable { 230 | private AppFilter appFilter; 231 | 232 | private void filter(List newItems) { 233 | DiffUtil.DiffResult diffResult = 234 | DiffUtil.calculateDiff(new AppDiffCallback(dataSet, newItems)); 235 | diffResult.dispatchUpdatesTo(this); 236 | dataSet.clear(); 237 | dataSet.addAll(newItems); 238 | } 239 | 240 | @NonNull 241 | @Override 242 | public AppViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 243 | LayoutAppItemBinding itemBinding = 244 | LayoutAppItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); 245 | itemBinding.getRoot().setOnClickListener(new AppClickListener()); 246 | return new AppViewHolder(itemBinding); 247 | } 248 | 249 | @Override 250 | public void onBindViewHolder(@NonNull AppViewHolder holder, int position) { 251 | holder.bind(dataSet.get(position)); 252 | } 253 | 254 | @Override 255 | public int getItemCount() { 256 | return dataSet.size(); 257 | } 258 | 259 | @Override 260 | public Filter getFilter() { 261 | if (appFilter == null) { 262 | appFilter = new AppFilter(); 263 | } 264 | return appFilter; 265 | } 266 | } 267 | 268 | private class AppFilter extends Filter { 269 | private final Context context; 270 | 271 | public AppFilter() { 272 | this.context = ExcludedAppsActivity.this; 273 | } 274 | 275 | @Override 276 | protected FilterResults performFiltering(CharSequence constraint) { 277 | FilterResults filterResults = new FilterResults(); 278 | 279 | if (TextUtils.isEmpty(constraint)) { 280 | filterResults.values = original; 281 | filterResults.count = original.size(); 282 | } else { 283 | List filteredDataSet = new ArrayList<>(); 284 | String query = 285 | constraint.toString().toUpperCase(Locale.getDefault()); // search both lower/upper case 286 | 287 | for (PackageInfo packageInfo : original) { 288 | if (context 289 | .getPackageManager() 290 | .getApplicationLabel(packageInfo.applicationInfo) 291 | .toString() 292 | .toUpperCase(Locale.getDefault()) 293 | .contains(query) 294 | || packageInfo.packageName.toUpperCase(Locale.US).contains(query)) { 295 | filteredDataSet.add(packageInfo); 296 | } 297 | } 298 | 299 | filterResults.values = filteredDataSet; 300 | filterResults.count = filteredDataSet.size(); 301 | } 302 | 303 | return filterResults; 304 | } 305 | 306 | @Override 307 | protected void publishResults(CharSequence constraint, FilterResults results) { 308 | adapter.filter((List) results.values); 309 | } 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /app/src/main/java/com/mokhtarabadi/tun2socks/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.widget.Toast; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.core.content.ContextCompat; 11 | 12 | import com.mokhtarabadi.tun2socks.sample.databinding.ActivityMainBinding; 13 | 14 | public class MainActivity extends AppCompatActivity { 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater()); 20 | setContentView(binding.getRoot()); 21 | 22 | binding.start.setOnClickListener( 23 | v -> { 24 | Intent intent = MainService.prepare(this); 25 | if (intent != null) { 26 | startActivityForResult(intent, 1); 27 | } else { 28 | toggleVpnService(true); 29 | } 30 | }); 31 | 32 | binding.stop.setOnClickListener(v -> toggleVpnService(false)); 33 | binding.settings.setOnClickListener( 34 | v -> startActivity(new Intent(MainActivity.this, SettingsActivity.class))); 35 | } 36 | 37 | @Override 38 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 39 | if (requestCode == 1 && resultCode == Activity.RESULT_OK) { 40 | toggleVpnService(true); 41 | } else { 42 | Toast.makeText(this, "Really!?", Toast.LENGTH_LONG).show(); 43 | } 44 | 45 | super.onActivityResult(requestCode, resultCode, data); 46 | } 47 | 48 | @Override 49 | public void onBackPressed() { 50 | moveTaskToBack(false); 51 | } 52 | 53 | private void toggleVpnService(boolean start) { 54 | Intent intent = new Intent(this, MainService.class); 55 | intent.setAction(start ? MainService.ACTION_START : MainService.ACTION_STOP); 56 | ContextCompat.startForegroundService(this, intent); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/mokhtarabadi/tun2socks/sample/MainApp.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | public class MainApp extends Application { 7 | 8 | public static volatile Context appContext; 9 | 10 | @Override 11 | public void onCreate() { 12 | super.onCreate(); 13 | 14 | appContext = getApplicationContext(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/mokhtarabadi/tun2socks/sample/MainNative.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import android.content.Context; 4 | import android.net.VpnService; 5 | import android.os.ParcelFileDescriptor; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | 9 | import androidx.annotation.Nullable; 10 | 11 | import com.getkeepsafe.relinker.ReLinker; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.Locale; 16 | 17 | public class MainNative { 18 | 19 | private static final String TAG = "tun2socks"; 20 | private static volatile boolean isInitialized = false; 21 | 22 | /** 23 | * Try to load native libraries 24 | * 25 | * @param context applicationContext 26 | */ 27 | public static void initialize(Context context) { 28 | if (isInitialized) { 29 | Log.w(TAG, "initialization before done"); 30 | return; 31 | } 32 | 33 | ReLinker.log(message -> Log.d(TAG, message)) 34 | .recursively() 35 | .loadLibrary( 36 | context, 37 | "native-lib", 38 | new ReLinker.LoadListener() { 39 | @Override 40 | public void success() { 41 | isInitialized = true; 42 | } 43 | 44 | @Override 45 | public void failure(Throwable t) { 46 | isInitialized = false; 47 | Log.e(TAG, "failed to load native libraries", t); 48 | } 49 | }); 50 | } 51 | 52 | /** 53 | * try to start badvpn-tunsocks (call this in separate thread) 54 | * 55 | * @param logLevel one of {@link LogLevel} 56 | * @param vpnInterfaceFileDescriptor the file descriptor after you called {@link 57 | * VpnService.Builder#establish()} 58 | * @param vpnInterfaceMtu tun mtu, also must set this value for vpn interface by calling {@link 59 | * VpnService.Builder#setMtu(int)} 60 | * @param socksServerAddress socks5 server address 61 | * @param socksServerPort socks5 server port 62 | * @param netIPv4Address an ipv4 address 63 | * @param netIPv6Address if not null, tun2socks will process ipv6 packets too 64 | * @param netmask netmask for example 255.255.255.0 65 | * @param forwardUdp if socks5 server support UDP, set this to true otherwise set to false 66 | * @return true of process finished successfully false if there is a problem! 67 | */ 68 | public static boolean startTun2Socks( 69 | LogLevel logLevel, 70 | ParcelFileDescriptor vpnInterfaceFileDescriptor, 71 | int vpnInterfaceMtu, 72 | String socksServerAddress, 73 | int socksServerPort, 74 | String netIPv4Address, 75 | @Nullable String netIPv6Address, 76 | String netmask, 77 | boolean forwardUdp) { 78 | // TODO: 9/26/21 "--dnsgw", "127.0.0.1:5353" 79 | 80 | ArrayList arguments = new ArrayList<>(); 81 | arguments.add("badvpn-tun2socks"); // app name (:D) 82 | arguments.addAll( 83 | Arrays.asList("--logger", "stdout")); // set logger to stdout so can see logs in logcat 84 | arguments.addAll( 85 | Arrays.asList("--loglevel", String.valueOf(logLevel.ordinal()))); // set log level 86 | arguments.addAll( 87 | Arrays.asList("--tunfd", String.valueOf(vpnInterfaceFileDescriptor.getFd()))); // set fd 88 | arguments.addAll(Arrays.asList("--tunmtu", String.valueOf(vpnInterfaceMtu))); 89 | arguments.addAll(Arrays.asList("--netif-ipaddr", netIPv4Address)); 90 | 91 | if (!TextUtils.isEmpty(netIPv6Address)) { 92 | arguments.addAll(Arrays.asList("--netif-ip6addr", netIPv6Address)); 93 | } 94 | 95 | arguments.addAll(Arrays.asList("--netif-netmask", netmask)); 96 | arguments.addAll( 97 | Arrays.asList( 98 | "--socks-server-addr", 99 | String.format(Locale.US, "%s:%d", socksServerAddress, socksServerPort))); 100 | 101 | if (forwardUdp) { 102 | arguments.add("--socks5-udp"); 103 | } 104 | 105 | int exitCode = start_tun2socks(arguments.toArray(new String[] {})); 106 | return exitCode == 0; 107 | } 108 | 109 | /** 110 | * start tun2socks with args 111 | * 112 | * @param args like when you run main() method on c! 113 | * @return other than zero mean failed 114 | */ 115 | private static native int start_tun2socks(String[] args); 116 | 117 | /** try to stop badvpn-tun2socks */ 118 | public static native void stopTun2Socks(); 119 | 120 | /** print usage help in logcat */ 121 | public static native void printTun2SocksHelp(); 122 | 123 | /** print version in logcat */ 124 | public static native void printTun2SocksVersion(); 125 | 126 | /** badvpn-tun2socks logLevel */ 127 | public enum LogLevel { 128 | NONE, // 0 129 | ERROR, // 1 130 | WARNING, // 2 131 | NOTICE, // 3 132 | INFO, // 4 133 | DEBUG // 5 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/mokhtarabadi/tun2socks/sample/MainService.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import android.app.Notification; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.net.ConnectivityManager; 9 | import android.net.Network; 10 | import android.net.NetworkCapabilities; 11 | import android.net.NetworkRequest; 12 | import android.net.VpnService; 13 | import android.os.Build; 14 | import android.os.ParcelFileDescriptor; 15 | import android.text.TextUtils; 16 | import android.util.Log; 17 | 18 | import androidx.annotation.NonNull; 19 | import androidx.annotation.RequiresApi; 20 | import androidx.core.app.NotificationChannelCompat; 21 | import androidx.core.app.NotificationCompat; 22 | import androidx.core.app.NotificationManagerCompat; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.IOException; 26 | import java.io.InputStreamReader; 27 | 28 | public class MainService extends VpnService { 29 | 30 | public static final String ACTION_START = "tun2socks_sample_start"; 31 | public static final String ACTION_STOP = "tun2socks_sample_stop"; 32 | 33 | private static final String TAG = "vpn_service"; 34 | 35 | private static final String PRIVATE_VLAN4_CLIENT = "10.0.0.1"; 36 | private static final String PRIVATE_VLAN4_ROUTER = "10.0.0.2"; 37 | 38 | private static final String PRIVATE_VLAN6_CLIENT = "fc00::1"; 39 | private static final String PRIVATE_VLAN6_ROUTER = "fc00::2"; 40 | 41 | private static final String PRIVATE_NETMASK = "255.255.255.252"; 42 | 43 | private static final int PRIVATE_MTU = 1500; 44 | 45 | private static volatile boolean isTun2SocksRunning = false; 46 | 47 | private ConnectivityManager connectivityManager; 48 | private Notification notification; 49 | private ParcelFileDescriptor descriptor; 50 | private Thread tun2SocksThread; 51 | 52 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1) 53 | private NetworkConnectivityMonitor networkConnectivityMonitor; 54 | private boolean networkConnectivityMonitorStarted = false; 55 | 56 | @Override 57 | public void onCreate() { 58 | super.onCreate(); 59 | 60 | MainNative.initialize(MainApp.appContext); // load native libraries... 61 | 62 | createNotification(); 63 | 64 | connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 65 | 66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { 67 | networkConnectivityMonitor = new NetworkConnectivityMonitor(); 68 | } 69 | } 70 | 71 | @Override 72 | public int onStartCommand(Intent intent, int flags, int startId) { 73 | if (intent != null && intent.getAction().equals(ACTION_STOP)) { 74 | stopService(); 75 | return START_NOT_STICKY; 76 | } 77 | startService(); 78 | return START_STICKY; 79 | } 80 | 81 | @Override 82 | public void onRevoke() { 83 | stopService(); 84 | } 85 | 86 | @Override 87 | public void onLowMemory() { 88 | stopService(); 89 | super.onLowMemory(); 90 | } 91 | 92 | @Override 93 | public void onDestroy() { 94 | stopForeground(true); 95 | 96 | super.onDestroy(); 97 | } 98 | 99 | private void createNotification() { 100 | NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); 101 | 102 | NotificationChannelCompat notificationChannel = 103 | new NotificationChannelCompat.Builder( 104 | "vpn_service", NotificationManagerCompat.IMPORTANCE_DEFAULT) 105 | .setName("Vpn service") 106 | .build(); 107 | notificationManager.createNotificationChannel(notificationChannel); 108 | 109 | Intent stopIntent = new Intent(this, MainService.class); 110 | stopIntent.setAction(ACTION_STOP); 111 | 112 | PendingIntent stopPendingIntent; 113 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 114 | stopPendingIntent = PendingIntent.getForegroundService(this, 1, stopIntent, 0); 115 | } else { 116 | stopPendingIntent = PendingIntent.getService(this, 1, stopIntent, 0); 117 | } 118 | 119 | PendingIntent contentPendingIntent = 120 | PendingIntent.getActivity(this, 2, new Intent(this, MainActivity.class), 0); 121 | 122 | notification = 123 | new NotificationCompat.Builder(this, notificationChannel.getId()) 124 | .setContentTitle("Vpn service") 125 | .setContentText("Testing Tun2Socks") 126 | .setSmallIcon(R.drawable.ic_baseline_vpn_lock_24) 127 | .addAction(R.drawable.ic_baseline_stop_24, "Stop", stopPendingIntent) 128 | .setOnlyAlertOnce(true) 129 | .setOngoing(true) 130 | .setAutoCancel(true) 131 | .setShowWhen(false) 132 | .setPriority(NotificationCompat.PRIORITY_DEFAULT) 133 | .setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) 134 | .setContentIntent(contentPendingIntent) 135 | .build(); 136 | } 137 | 138 | private void startService() { 139 | if (isTun2SocksRunning) { 140 | Log.w(TAG, "seems vpn already running!?"); 141 | return; 142 | } 143 | 144 | startForeground(1, notification); 145 | 146 | boolean supportIPv4 = PreferenceHelper.isSupportIPv4(); 147 | boolean supportIPv6 = PreferenceHelper.isSupportIPv6(); 148 | 149 | Builder builder = new Builder().setSession("Tun2Socks").setMtu(PRIVATE_MTU); 150 | 151 | if (supportIPv4) { 152 | builder.addAddress(PRIVATE_VLAN4_CLIENT, 30); 153 | for (String iPv4DNSServer : PreferenceHelper.getIPv4DNSServers()) { 154 | builder.addDnsServer(iPv4DNSServer); 155 | } 156 | } 157 | 158 | if (supportIPv6) { 159 | builder.addAddress(PRIVATE_VLAN6_CLIENT, 126); 160 | for (String iPv6DNSServer : PreferenceHelper.getIPv6DNSServers()) { 161 | builder.addDnsServer(iPv6DNSServer); 162 | } 163 | } 164 | 165 | if (PreferenceHelper.needBypassLan()) { 166 | if (supportIPv4) { 167 | try (BufferedReader reader = 168 | new BufferedReader(new InputStreamReader(getAssets().open("bypass_private_ips.txt")))) { 169 | String line; 170 | while ((line = reader.readLine()) != null) { 171 | if (!TextUtils.isEmpty(line.trim())) { 172 | String[] ipNetmask = line.split("/"); 173 | builder.addRoute(ipNetmask[0], Integer.parseInt(ipNetmask[1])); 174 | } 175 | } 176 | } catch (IOException e) { 177 | e.printStackTrace(); 178 | } 179 | } 180 | 181 | if (supportIPv6) { 182 | builder.addRoute("2000::", 3); 183 | } 184 | } else { 185 | if (supportIPv4) { 186 | builder.addRoute("0.0.0.0", 0); 187 | } 188 | 189 | if (supportIPv6) { 190 | builder.addRoute("::", 0); 191 | } 192 | } 193 | 194 | // TODO: 9/26/21 support --dnsgw option (need implements in native code) 195 | 196 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 197 | try { 198 | for (String excludedApp : PreferenceHelper.getExcludedApps()) { 199 | builder.addDisallowedApplication(excludedApp); 200 | } 201 | } catch (PackageManager.NameNotFoundException e) { 202 | e.printStackTrace(); 203 | } 204 | } 205 | 206 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 207 | builder.setMetered(false); 208 | } 209 | 210 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 211 | Network activeNetwork = connectivityManager.getActiveNetwork(); 212 | if (activeNetwork != null) { 213 | builder.setUnderlyingNetworks(new Network[] {activeNetwork}); 214 | } 215 | } 216 | 217 | descriptor = builder.establish(); 218 | if (descriptor == null) { 219 | stopSelf(); // !? 220 | return; 221 | } 222 | 223 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { 224 | startNetworkConnectivityMonitor(); 225 | } 226 | 227 | tun2SocksThread = 228 | new Thread( 229 | () -> { 230 | isTun2SocksRunning = true; 231 | 232 | boolean result = 233 | MainNative.startTun2Socks( 234 | MainNative.LogLevel.INFO, 235 | descriptor, 236 | PRIVATE_MTU, 237 | PreferenceHelper.getSocksServerAddress(), 238 | PreferenceHelper.getSocksServerPort(), 239 | PRIVATE_VLAN4_ROUTER, 240 | PRIVATE_VLAN6_ROUTER, 241 | PRIVATE_NETMASK, 242 | PreferenceHelper.isSocksSupportUDP()); 243 | Log.d(TAG, "tun2socks stopped, result: " + result); 244 | 245 | isTun2SocksRunning = false; 246 | }); 247 | tun2SocksThread.start(); 248 | } 249 | 250 | private void stopService() { 251 | if (!isTun2SocksRunning) { 252 | Log.w(TAG, "seems already stopped!?"); 253 | return; 254 | } 255 | 256 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { 257 | stopNetworkConnectivityMonitor(); 258 | } 259 | 260 | MainNative.stopTun2Socks(); 261 | try { 262 | tun2SocksThread.join(); 263 | descriptor.close(); 264 | } catch (IOException | InterruptedException e) { 265 | e.printStackTrace(); 266 | } 267 | descriptor = null; 268 | 269 | stopSelf(); 270 | } 271 | 272 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1) 273 | private void startNetworkConnectivityMonitor() { 274 | NetworkRequest.Builder builder = 275 | new NetworkRequest.Builder() 276 | .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 277 | .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 278 | 279 | if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { // workarounds for OEM bugs 280 | builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); 281 | builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); 282 | } 283 | NetworkRequest request = builder.build(); 284 | 285 | try { 286 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { 287 | connectivityManager.registerNetworkCallback(request, networkConnectivityMonitor); 288 | } else { 289 | connectivityManager.requestNetwork(request, networkConnectivityMonitor); 290 | } 291 | networkConnectivityMonitorStarted = true; 292 | } catch (SecurityException se) { 293 | se.printStackTrace(); 294 | } 295 | } 296 | 297 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1) 298 | private void stopNetworkConnectivityMonitor() { 299 | try { 300 | if (networkConnectivityMonitorStarted) { 301 | connectivityManager.unregisterNetworkCallback(networkConnectivityMonitor); 302 | networkConnectivityMonitorStarted = false; 303 | } 304 | } catch (Exception e) { 305 | // Ignore, monitor not installed if the connectivity checks failed. 306 | } 307 | } 308 | 309 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1) 310 | private class NetworkConnectivityMonitor extends ConnectivityManager.NetworkCallback { 311 | @Override 312 | public void onAvailable(@NonNull Network network) { 313 | setUnderlyingNetworks(new Network[] {network}); 314 | } 315 | 316 | @Override 317 | public void onCapabilitiesChanged( 318 | @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { 319 | setUnderlyingNetworks(new Network[] {network}); 320 | } 321 | 322 | @Override 323 | public void onLost(@NonNull Network network) { 324 | setUnderlyingNetworks(null); 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /app/src/main/java/com/mokhtarabadi/tun2socks/sample/PreferenceHelper.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | import com.frybits.harmony.Harmony; 6 | 7 | import java.util.Arrays; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class PreferenceHelper { 12 | 13 | public static final String PREFERENCE_SUPPORT_IPV4 = "support_ipv4"; 14 | public static final String PREFERENCE_SUPPORT_IPV6 = "support_ipv6"; 15 | public static final String PREFERENCE_IPV4_DNS_SERVERS = "ipv4_dns_servers"; 16 | public static final String PREFERENCE_IPV6_DNS_SERVERS = "ipv6_dns_servers"; 17 | public static final String PREFERENCE_SOCKS_SERVER = "socks_server"; 18 | public static final String PREFERENCE_SOCKS_PORT = "socks_port"; 19 | public static final String PREFERENCE_SUPPORT_UDP = "socks_support_udp"; 20 | public static final String PREFERENCE_BYPASS_LAN = "bypass_lan"; 21 | public static final String PREFERENCE_EXCLUDED_APPS = "excluded_apps"; 22 | 23 | public static final SharedPreferences preferences = 24 | Harmony.getSharedPreferences(MainApp.appContext, "main"); 25 | 26 | public static boolean isSupportIPv4() { 27 | return preferences.getBoolean(PREFERENCE_SUPPORT_IPV4, true); 28 | } 29 | 30 | public static boolean isSupportIPv6() { 31 | return preferences.getBoolean(PREFERENCE_SUPPORT_IPV6, true); 32 | } 33 | 34 | public static Set getIPv4DNSServers() { 35 | return preferences.getStringSet( 36 | PREFERENCE_IPV4_DNS_SERVERS, new HashSet<>(Arrays.asList("1.0.0.1", "1.1.1.1"))); 37 | } 38 | 39 | public static Set getIPv6DNSServers() { 40 | return preferences.getStringSet( 41 | PREFERENCE_IPV6_DNS_SERVERS, 42 | new HashSet<>(Arrays.asList("2606:4700:4700::1001", "2606:4700:4700::1111"))); 43 | } 44 | 45 | public static String getSocksServerAddress() { 46 | return preferences.getString(PREFERENCE_SOCKS_SERVER, "127.0.0.1"); 47 | } 48 | 49 | public static int getSocksServerPort() { 50 | return preferences.getInt(PREFERENCE_SOCKS_PORT, 1080); 51 | } 52 | 53 | public static boolean isSocksSupportUDP() { 54 | return preferences.getBoolean(PREFERENCE_SUPPORT_UDP, true); 55 | } 56 | 57 | public static boolean needBypassLan() { 58 | return preferences.getBoolean(PREFERENCE_BYPASS_LAN, true); 59 | } 60 | 61 | public static Set getExcludedApps() { 62 | return preferences.getStringSet(PREFERENCE_EXCLUDED_APPS, new HashSet<>()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/mokhtarabadi/tun2socks/sample/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_BYPASS_LAN; 4 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_IPV4_DNS_SERVERS; 5 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_IPV6_DNS_SERVERS; 6 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_SOCKS_PORT; 7 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_SOCKS_SERVER; 8 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_SUPPORT_IPV4; 9 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_SUPPORT_IPV6; 10 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.PREFERENCE_SUPPORT_UDP; 11 | import static com.mokhtarabadi.tun2socks.sample.PreferenceHelper.preferences; 12 | 13 | import android.content.Intent; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.text.TextUtils; 17 | 18 | import androidx.annotation.Nullable; 19 | import androidx.appcompat.app.AppCompatActivity; 20 | 21 | import com.mokhtarabadi.tun2socks.sample.databinding.ActivitySettingsBinding; 22 | 23 | import java.util.Arrays; 24 | import java.util.HashSet; 25 | 26 | public class SettingsActivity extends AppCompatActivity { 27 | 28 | private ActivitySettingsBinding binding; 29 | 30 | @Override 31 | protected void onCreate(@Nullable Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | binding = ActivitySettingsBinding.inflate(getLayoutInflater()); 34 | setContentView(binding.getRoot()); 35 | 36 | binding.reset.setOnClickListener( 37 | v -> { 38 | preferences.edit().clear().apply(); 39 | updateFromPreferences(); 40 | }); 41 | 42 | binding.excludedApps.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); 43 | binding.excludedApps.setOnClickListener( 44 | v -> startActivity(new Intent(SettingsActivity.this, ExcludedAppsActivity.class))); 45 | 46 | updateFromPreferences(); 47 | } 48 | 49 | @Override 50 | protected void onDestroy() { 51 | saveFromUI(); 52 | 53 | super.onDestroy(); 54 | } 55 | 56 | private void updateFromPreferences() { 57 | binding.supportIpv4.setChecked(PreferenceHelper.isSupportIPv4()); 58 | binding.supportIpv6.setChecked(PreferenceHelper.isSupportIPv6()); 59 | binding.ipv4DnsServers.setText(TextUtils.join(",", PreferenceHelper.getIPv4DNSServers())); 60 | binding.ipv6DnsServers.setText(TextUtils.join(",", PreferenceHelper.getIPv6DNSServers())); 61 | binding.socksAddress.setText(PreferenceHelper.getSocksServerAddress()); 62 | binding.socksPort.setText(String.valueOf(PreferenceHelper.getSocksServerPort())); 63 | binding.supportUdp.setChecked(PreferenceHelper.isSocksSupportUDP()); 64 | binding.bypassLan.setChecked(PreferenceHelper.needBypassLan()); 65 | } 66 | 67 | private void saveFromUI() { 68 | preferences 69 | .edit() 70 | .putBoolean(PREFERENCE_SUPPORT_IPV4, binding.supportIpv4.isChecked()) 71 | .putBoolean(PREFERENCE_SUPPORT_IPV6, binding.supportIpv6.isChecked()) 72 | .putStringSet( 73 | PREFERENCE_IPV4_DNS_SERVERS, 74 | new HashSet<>( 75 | Arrays.asList(binding.ipv4DnsServers.getText().toString().trim().split(",")))) 76 | .putStringSet( 77 | PREFERENCE_IPV6_DNS_SERVERS, 78 | new HashSet<>( 79 | Arrays.asList(binding.ipv6DnsServers.getText().toString().trim().split(",")))) 80 | .putString(PREFERENCE_SOCKS_SERVER, binding.socksAddress.getText().toString().trim()) 81 | .putInt(PREFERENCE_SOCKS_PORT, Integer.parseInt(binding.socksPort.getText().toString())) 82 | .putBoolean(PREFERENCE_SUPPORT_UDP, binding.supportUdp.isChecked()) 83 | .putBoolean(PREFERENCE_BYPASS_LAN, binding.bypassLan.isChecked()) 84 | .apply(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_stop_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_vpn_lock_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_excluded_apps.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 23 | 24 | 25 | 26 | 30 | 31 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | 32 | 36 | 37 | 38 | 43 | 44 | 49 | 50 | 54 | 55 | 56 | 61 | 62 | 65 | 66 | 70 | 71 | 75 | 76 | 77 | 78 | 83 | 84 | 89 | 90 | 91 | 92 | 98 | 99 | 103 | 104 | 110 | 111 | 116 | 117 | 123 | 124 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_app_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 22 | 23 | 36 | 37 | 50 | 51 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Universal Android Tun2Socks 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/test/java/com/mokhtarabadi/tun2socks/sample/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.mokhtarabadi.tun2socks.sample; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:7.0.2" 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | task clean(type: Delete) { 16 | delete rootProject.buildDir 17 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhtarabadi/universal-android-tun2socks/332c96c4d6438a71447cb7b2f0f730c59cd6d414/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Sep 23 14:29:52 IRST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() // Warning: this repository is going to shut down soon 7 | } 8 | } 9 | rootProject.name = "Universal Android Tun2Socks" 10 | include ':app' 11 | --------------------------------------------------------------------------------