├── .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 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 |
--------------------------------------------------------------------------------