8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # V2ray-Example
2 |
3 | A simple Java module with sample source code to implement the v2ray on Android.
4 | This module is built from a [custom library based on the xray core](https://github.com/dev7dev/AndroidLibXrayLite) . With this module, you can easily develop your VPN application based on powerful v2ray protocols. This module currently supports v2ray short links (URI) thanks to a custom library (may have bugs).
5 | In fact, this module provides you a v2ray client with xray core, which you can manage with static functions in [V2rayController](https://github.com/dev7dev/V2ray-Android/blob/main/v2ray/src/main/java/dev/dev7/lib/v2ray/V2rayController.java) .
6 |
7 | | :exclamation: The function of the library to convert uri to json may have a bug. Test before using in production. |
8 | |----------------------------------------------------------------------------------------------------------------------|
9 |
10 | **Sample Application**
11 | *You can download sample release of this module from [Github Releases of this repo](https://github.com/dev7dev/V2ray-Android/releases)*
12 |
13 |
14 |
15 |
16 |
17 | ## Build
18 | *Before anything, make sure you are using the latest version of Android Studio.*
19 | *The sample project does not use any special dependencies or tools, so if there are no network or software issues on your system, you can simply clone it and build it in Android Studio with one click.*
20 |
21 | ## Implementation
22 | - First, check that the version of gradle and build config of your project is compatible with the version used in this source.
23 | - Clone or download this repo.
24 | - Open your project in android studio and navigate to `File` -> `New` -> `Import module`. Then select the source directory to `v2ray` from the cloned repository.
25 | - After Gradle build , Open [setting.gradle](https://github.com/dev7dev/V2ray-Android/blob/main/settings.gradle#L13) file in your project and add this to your Gradle repositories block.
26 | ```
27 | flatDir {
28 | dirs 'v2ray/libs'
29 | }
30 | ```
31 | - Add module as a dependency to your main project by adding this line to app-level [build.gradle](https://github.com/dev7dev/V2ray-Android/blob/main/app/build.gradle#L35).
32 | ```
33 | implementation project(path: ':v2ray')
34 | ```
35 | - Click on `Rebuild project` from `Build` Tab .
36 |
37 | > Note : *The contents of the manifest of your main project will be automatically merged with the contents of the [library monifest](https://github.com/dev7dev/V2ray-Android/blob/main/v2ray/src/main/AndroidManifest.xml), so there is no need to manually copy the contents.*
38 | ## Usage
39 | - First, the library needs to be initialized. So call [this function](https://github.com/dev7dev/V2ray-Android/blob/main/v2ray/src/main/java/dev/dev7/lib/v2ray/V2rayController.java#L62) for this purpose in the primary methods of your activity.
40 | ```
41 | V2rayController.init(this, R.drawable.ic_launcher, "V2ray Android");
42 | ```
43 | > `R.drawable.ic_launcher` is a drawable resource that will be used in vpn notification.
44 | > `"V2ray Android"` is application name
45 | - You can get the version of core with this
46 | ```
47 | V2rayController.getCoreVersion()
48 | ```
49 | - With this function you will get current status of module
50 | ```
51 | V2rayController.getConnectionState()
52 | ```
53 | > This will return an enum with three state : CONNECTED,CONNECTING,DISCONNECTED
54 | - For **start a connection **
55 | ```
56 | V2rayController.startV2ray(this, "Test Server", "vless:\\test@test", null);
57 | ```
58 | > `"Test Server"` will be used as remark in notification.
59 | > `"vless:\\test@test"` is a string of your config (could be uri or full json)
60 | > `null` means no application package will be bypass the tunnel
61 | - For **stop connection**
62 | ```
63 | V2rayController.stopV2ray(this);
64 | ```
65 | > you should call this function in main thread.
66 |
67 | ## Credits
68 | - https://github.com/xtls/xray-core
69 | - https://github.com/2dust/AndroidLibXrayLite
70 | - https://github.com/gvcgo/vpnparser
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/app/.DS_Store
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | namespace 'dev.dev7.example'
7 | compileSdk 34
8 |
9 | defaultConfig {
10 | applicationId "dev.dev7.example"
11 | minSdk 21
12 | targetSdk 34
13 | versionCode 11
14 | versionName "8.1823"
15 | setProperty("archivesBaseName", rootProject.name + "-" + versionName)
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled true
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | buildFeatures {
25 | buildConfig = true
26 | }
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_1_8
29 | targetCompatibility JavaVersion.VERSION_1_8
30 | }
31 |
32 | }
33 |
34 | dependencies {
35 | implementation 'androidx.appcompat:appcompat:1.6.1'
36 | implementation project(path: ':v2ray')
37 | }
--------------------------------------------------------------------------------
/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/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/app/src/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/app/src/main/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/dev7/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.example;
2 |
3 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_CONNECTION_STATE_BROADCAST_EXTRA;
4 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_DOWNLOAD_SPEED_BROADCAST_EXTRA;
5 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_DOWNLOAD_TRAFFIC_BROADCAST_EXTRA;
6 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_DURATION_BROADCAST_EXTRA;
7 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_UPLOAD_SPEED_BROADCAST_EXTRA;
8 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_UPLOAD_TRAFFIC_BROADCAST_EXTRA;
9 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_STATICS_BROADCAST_INTENT;
10 |
11 | import android.annotation.SuppressLint;
12 | import android.content.BroadcastReceiver;
13 | import android.content.Context;
14 | import android.content.Intent;
15 | import android.content.IntentFilter;
16 | import android.content.SharedPreferences;
17 | import android.os.Build;
18 | import android.os.Bundle;
19 | import android.os.Handler;
20 | import android.widget.Button;
21 | import android.widget.EditText;
22 | import android.widget.TextView;
23 |
24 | import androidx.appcompat.app.AppCompatActivity;
25 |
26 | import java.util.Objects;
27 |
28 | import dev.dev7.lib.v2ray.V2rayController;
29 | import dev.dev7.lib.v2ray.utils.V2rayConfigs;
30 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
31 |
32 | public class MainActivity extends AppCompatActivity {
33 |
34 | private Button connection;
35 | private TextView connection_speed, connection_traffic, connection_time, server_delay, connected_server_delay, connection_mode,core_version;
36 | private EditText v2ray_config;
37 | private SharedPreferences sharedPreferences;
38 | private BroadcastReceiver v2rayBroadCastReceiver;
39 |
40 | @SuppressLint({"SetTextI18n", "UnspecifiedRegisterReceiverFlag"})
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setContentView(R.layout.activity_main);
45 | if (savedInstanceState == null) {
46 | V2rayController.init(this, R.drawable.ic_launcher, "V2ray Android");
47 | connection = findViewById(R.id.btn_connection);
48 | connection_speed = findViewById(R.id.connection_speed);
49 | connection_time = findViewById(R.id.connection_duration);
50 | connection_traffic = findViewById(R.id.connection_traffic);
51 | server_delay = findViewById(R.id.server_delay);
52 | connection_mode = findViewById(R.id.connection_mode);
53 | connected_server_delay = findViewById(R.id.connected_server_delay);
54 | v2ray_config = findViewById(R.id.v2ray_config);
55 | core_version = findViewById(R.id.core_version);
56 | }
57 |
58 | core_version.setText(V2rayController.getCoreVersion());
59 | // initialize shared preferences for save or reload default config
60 | sharedPreferences = getSharedPreferences("conf", MODE_PRIVATE);
61 | // reload previous config to edit text
62 | v2ray_config.setText(sharedPreferences.getString("v2ray_config", getDefaultConfig()));
63 | connection.setOnClickListener(view -> {
64 | sharedPreferences.edit().putString("v2ray_config", v2ray_config.getText().toString()).apply();
65 | if (V2rayController.getConnectionState() == V2rayConstants.CONNECTION_STATES.DISCONNECTED) {
66 | V2rayController.startV2ray(this, "Test Server", v2ray_config.getText().toString(), null);
67 | } else {
68 | V2rayController.stopV2ray(this);
69 | }
70 | });
71 |
72 |
73 | // Check the connection delay of connected config.
74 | connected_server_delay.setOnClickListener(view -> {
75 | connected_server_delay.setText("connected server delay : measuring...");
76 | // Don`t forget to do ui jobs in ui thread!
77 | V2rayController.getConnectedV2rayServerDelay(this, delayResult -> runOnUiThread(() -> connected_server_delay.setText("connected server delay : " + delayResult + "ms")));
78 | });
79 | // Another way to check the connection delay of a config without connecting to it.
80 | server_delay.setOnClickListener(view -> {
81 | server_delay.setText("server delay : measuring...");
82 | new Handler().postDelayed(() -> server_delay.setText("server delay : " + V2rayController.getV2rayServerDelay(v2ray_config.getText().toString()) + "ms"), 200);
83 | });
84 |
85 | connection_mode.setOnClickListener(view -> {
86 | V2rayController.toggleConnectionMode();
87 | connection_mode.setText("connection mode : " + V2rayConfigs.serviceMode.toString());
88 | });
89 |
90 | // Check connection state when activity launch
91 | switch (V2rayController.getConnectionState()) {
92 | case CONNECTED:
93 | connection.setText("CONNECTED");
94 | // check connection latency
95 | connected_server_delay.callOnClick();
96 | break;
97 | case DISCONNECTED:
98 | connection.setText("DISCONNECTED");
99 | break;
100 | case CONNECTING:
101 | connection.setText("CONNECTING");
102 | break;
103 | default:
104 | break;
105 | }
106 | //I tested several different ways to send information from the connection process side
107 | // to other places (such as interfaces, AIDL and singleton ,...) apparently the best way
108 | // to send information is broadcast.
109 | // So v2ray library will be broadcast information with action V2RAY_CONNECTION_INFO.
110 | v2rayBroadCastReceiver = new BroadcastReceiver() {
111 | @Override
112 | public void onReceive(Context context, Intent intent) {
113 | runOnUiThread(() -> {
114 | connection_time.setText("connection time : " + Objects.requireNonNull(intent.getExtras()).getString(SERVICE_DURATION_BROADCAST_EXTRA));
115 | connection_speed.setText("connection speed : " + intent.getExtras().getString(SERVICE_UPLOAD_SPEED_BROADCAST_EXTRA) + " | " + intent.getExtras().getString(SERVICE_DOWNLOAD_SPEED_BROADCAST_EXTRA));
116 | connection_traffic.setText("connection traffic : " + intent.getExtras().getString(SERVICE_UPLOAD_TRAFFIC_BROADCAST_EXTRA) + " | " + intent.getExtras().getString(SERVICE_DOWNLOAD_TRAFFIC_BROADCAST_EXTRA));
117 | connection_mode.setText("connection mode : " + V2rayConfigs.serviceMode.toString());
118 | switch ((V2rayConstants.CONNECTION_STATES) Objects.requireNonNull(intent.getExtras().getSerializable(SERVICE_CONNECTION_STATE_BROADCAST_EXTRA))) {
119 | case CONNECTED:
120 | connection.setText("CONNECTED");
121 | break;
122 | case DISCONNECTED:
123 | connection.setText("DISCONNECTED");
124 | connected_server_delay.setText("connected server delay : wait for connection");
125 | break;
126 | case CONNECTING:
127 | connection.setText("CONNECTING");
128 | break;
129 | default:
130 | break;
131 | }
132 | });
133 | }
134 | };
135 |
136 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
137 | registerReceiver(v2rayBroadCastReceiver, new IntentFilter(V2RAY_SERVICE_STATICS_BROADCAST_INTENT), RECEIVER_EXPORTED);
138 | } else {
139 | registerReceiver(v2rayBroadCastReceiver, new IntentFilter(V2RAY_SERVICE_STATICS_BROADCAST_INTENT));
140 | }
141 | }
142 |
143 | public static String getDefaultConfig() {
144 | return "";
145 | }
146 |
147 |
148 | @Override
149 | protected void onDestroy() {
150 | super.onDestroy();
151 | if (v2rayBroadCastReceiver != null){
152 | unregisterReceiver(v2rayBroadCastReceiver);
153 | }
154 | }
155 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/dev7/example/MainApplication.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.example;
2 |
3 | import android.app.Application;
4 |
5 | public class MainApplication extends Application {
6 | @Override
7 | public void onCreate() {
8 | super.onCreate();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/app/src/main/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
28 |
29 |
30 |
34 |
35 |
43 |
44 |
52 |
53 |
54 |
62 |
63 |
71 |
72 |
73 |
81 |
82 |
90 |
91 |
92 |
93 |
101 |
102 |
111 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF000000
6 | #FFFFFFFF
7 | #302F2F
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | V2ray-Example
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | \
9 |
10 |
16 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id 'com.android.application' version '8.5.0' apply false
4 | id 'com.android.library' version '8.5.0' apply false
5 | }
--------------------------------------------------------------------------------
/connected.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/connected.jpeg
--------------------------------------------------------------------------------
/disconnected.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/disconnected.jpg
--------------------------------------------------------------------------------
/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. For more details, visit
12 | # https://developer.android.com/r/tools/gradle-multi-project-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 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Apr 04 03:49:51 IRST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
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 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Thu Apr 18 21:42:40 IRST 2024
8 | sdk.dir=C\:\\Users\\developer\\AppData\\Local\\Android\\Sdk
9 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | flatDir {
14 | dirs 'v2ray/libs'
15 | }
16 | }
17 | }
18 |
19 | rootProject.name = "V2ray-Android"
20 | include ':app'
21 | include ':v2ray'
22 |
--------------------------------------------------------------------------------
/v2ray/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/v2ray/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | }
4 |
5 | android {
6 | namespace 'dev.dev7.lib.v2ray'
7 | compileSdk 34
8 |
9 | defaultConfig {
10 | minSdk 21
11 | }
12 |
13 | sourceSets {
14 | main {
15 | jniLibs.srcDirs = ['libs']
16 | }
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_1_8
27 | targetCompatibility JavaVersion.VERSION_1_8
28 | }
29 | }
30 |
31 | dependencies {
32 | implementation 'androidx.appcompat:appcompat:1.6.1'
33 | implementation(group: "", name: "libv2ray", version: "", ext: "aar")
34 | }
--------------------------------------------------------------------------------
/v2ray/libs/arm64-v8a/libtun2socks.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/v2ray/libs/arm64-v8a/libtun2socks.so
--------------------------------------------------------------------------------
/v2ray/libs/armeabi-v7a/libtun2socks.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/v2ray/libs/armeabi-v7a/libtun2socks.so
--------------------------------------------------------------------------------
/v2ray/libs/libv2ray.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/v2ray/libs/libv2ray.aar
--------------------------------------------------------------------------------
/v2ray/libs/x86/libtun2socks.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/v2ray/libs/x86/libtun2socks.so
--------------------------------------------------------------------------------
/v2ray/libs/x86_64/libtun2socks.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev7dev/V2ray-Android/fc15bb8e145a181c8859090a592be589efc8e92a/v2ray/libs/x86_64/libtun2socks.so
--------------------------------------------------------------------------------
/v2ray/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
--------------------------------------------------------------------------------
/v2ray/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
19 |
26 |
27 |
28 |
29 |
32 |
35 |
36 |
37 |
38 |
45 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/V2rayController.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray;
2 |
3 | import static android.Manifest.permission.POST_NOTIFICATIONS;
4 | import static android.content.Context.RECEIVER_EXPORTED;
5 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_COMMAND_EXTRA;
6 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_COMMAND_INTENT;
7 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_CONFIG_EXTRA;
8 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_CONNECTION_STATE_BROADCAST_EXTRA;
9 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_EXTRA;
10 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_INTENT;
11 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_STATICS_BROADCAST_INTENT;
12 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_TYPE_BROADCAST_EXTRA;
13 | import static dev.dev7.lib.v2ray.utils.V2rayConfigs.connectionState;
14 | import static dev.dev7.lib.v2ray.utils.V2rayConfigs.currentConfig;
15 | import static dev.dev7.lib.v2ray.utils.V2rayConfigs.serviceMode;
16 |
17 | import android.annotation.SuppressLint;
18 | import android.app.Activity;
19 | import android.content.BroadcastReceiver;
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.content.IntentFilter;
23 | import android.net.VpnService;
24 | import android.os.Build;
25 | import android.widget.Toast;
26 |
27 | import androidx.activity.result.ActivityResultLauncher;
28 | import androidx.activity.result.contract.ActivityResultContracts;
29 | import androidx.appcompat.app.AppCompatActivity;
30 | import androidx.core.app.ActivityCompat;
31 | import androidx.core.content.ContextCompat;
32 | import androidx.core.content.PermissionChecker;
33 |
34 | import java.util.ArrayList;
35 | import java.util.Objects;
36 |
37 | import dev.dev7.lib.v2ray.core.V2rayCoreExecutor;
38 | import dev.dev7.lib.v2ray.interfaces.LatencyDelayListener;
39 | import dev.dev7.lib.v2ray.services.V2rayVPNService;
40 | import dev.dev7.lib.v2ray.services.V2rayProxyService;
41 | import dev.dev7.lib.v2ray.utils.V2rayConfigs;
42 | import dev.dev7.lib.v2ray.utils.Utilities;
43 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
44 | import libv2ray.Libv2ray;
45 |
46 | public class V2rayController {
47 | private static ActivityResultLauncher activityResultLauncher;
48 | static final BroadcastReceiver stateUpdaterBroadcastReceiver = new BroadcastReceiver() {
49 | @Override
50 | public void onReceive(Context context, Intent intent) {
51 | try {
52 | connectionState = (V2rayConstants.CONNECTION_STATES) Objects.requireNonNull(intent.getExtras()).getSerializable(SERVICE_CONNECTION_STATE_BROADCAST_EXTRA);
53 | if (Objects.equals(intent.getExtras().getString(SERVICE_TYPE_BROADCAST_EXTRA), V2rayProxyService.class.getSimpleName())) {
54 | V2rayConfigs.serviceMode = V2rayConstants.SERVICE_MODES.PROXY_MODE;
55 | } else {
56 | V2rayConfigs.serviceMode = V2rayConstants.SERVICE_MODES.VPN_MODE;
57 | }
58 | } catch (Exception ignore) {}
59 | }
60 | };
61 |
62 | public static void init(final AppCompatActivity activity, final int app_icon, final String app_name) {
63 | Utilities.copyAssets(activity);
64 | currentConfig.applicationIcon = app_icon;
65 | currentConfig.applicationName = app_name;
66 | registerReceivers(activity);
67 | activityResultLauncher = activity.registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
68 | if (result.getResultCode() == Activity.RESULT_OK) {
69 | startTunnel(activity);
70 | } else {
71 | Toast.makeText(activity, "Permission not granted.", Toast.LENGTH_LONG).show();
72 | }
73 | });
74 | }
75 |
76 | @SuppressLint("UnspecifiedRegisterReceiverFlag")
77 | public static void registerReceivers(final Activity activity){
78 | try {
79 | activity.unregisterReceiver(stateUpdaterBroadcastReceiver);
80 | }catch (Exception ignore){}
81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
82 | activity.registerReceiver(stateUpdaterBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_STATICS_BROADCAST_INTENT), RECEIVER_EXPORTED);
83 | } else {
84 | activity.registerReceiver(stateUpdaterBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_STATICS_BROADCAST_INTENT));
85 | }
86 | }
87 |
88 | public static V2rayConstants.CONNECTION_STATES getConnectionState() {
89 | return connectionState;
90 | }
91 |
92 | public static boolean isPreparedForConnection(final Context context) {
93 | if (Build.VERSION.SDK_INT >= 33) {
94 | if (ContextCompat.checkSelfPermission(context, POST_NOTIFICATIONS) != PermissionChecker.PERMISSION_GRANTED) {
95 | return false;
96 | }
97 | }
98 | Intent vpnServicePrepareIntent = VpnService.prepare(context);
99 | return vpnServicePrepareIntent == null;
100 | }
101 |
102 | private static void prepareForConnection(final Activity activity) {
103 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
104 | if (ContextCompat.checkSelfPermission(activity, POST_NOTIFICATIONS) != PermissionChecker.PERMISSION_GRANTED) {
105 | ActivityCompat.requestPermissions(activity, new String[]{POST_NOTIFICATIONS}, 101);
106 | return;
107 | }
108 | }
109 | Intent vpnServicePrepareIntent = VpnService.prepare(activity);
110 | if (vpnServicePrepareIntent != null) {
111 | activityResultLauncher.launch(vpnServicePrepareIntent);
112 | }
113 | }
114 |
115 | public static void startV2ray(final Activity activity, final String remark, final String config, final ArrayList blocked_apps) {
116 | if (!Utilities.refillV2rayConfig(remark, config, blocked_apps)) {
117 | return;
118 | }
119 | if (!isPreparedForConnection(activity)) {
120 | prepareForConnection(activity);
121 | } else {
122 | startTunnel(activity);
123 | }
124 | }
125 |
126 | public static void stopV2ray(final Context context) {
127 | Intent stop_intent = new Intent(V2RAY_SERVICE_COMMAND_INTENT);
128 | stop_intent.setPackage(context.getPackageName());
129 | stop_intent.putExtra(V2RAY_SERVICE_COMMAND_EXTRA, V2rayConstants.SERVICE_COMMANDS.STOP_SERVICE);
130 | context.sendBroadcast(stop_intent);
131 | }
132 |
133 | @SuppressLint("UnspecifiedRegisterReceiverFlag")
134 | public static void getConnectedV2rayServerDelay(final Context context, final LatencyDelayListener latencyDelayCallback) {
135 | if (getConnectionState() != V2rayConstants.CONNECTION_STATES.CONNECTED) {
136 | latencyDelayCallback.OnResultReady(-1);
137 | return;
138 | }
139 | BroadcastReceiver connectionLatencyBroadcastReceiver = new BroadcastReceiver() {
140 | @Override
141 | public void onReceive(Context context, Intent intent) {
142 | try {
143 | int delay = Objects.requireNonNull(intent.getExtras()).getInt(V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_EXTRA);
144 | latencyDelayCallback.OnResultReady(delay);
145 | } catch (Exception ignore) {
146 | latencyDelayCallback.OnResultReady(-1);
147 | }
148 | context.unregisterReceiver(this);
149 | }
150 | };
151 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
152 | context.registerReceiver(connectionLatencyBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_INTENT), RECEIVER_EXPORTED);
153 | } else {
154 | context.registerReceiver(connectionLatencyBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_INTENT));
155 | }
156 | Intent get_delay_intent = new Intent(V2RAY_SERVICE_COMMAND_INTENT);
157 | get_delay_intent.setPackage(context.getPackageName());
158 | get_delay_intent.putExtra(V2RAY_SERVICE_COMMAND_EXTRA, V2rayConstants.SERVICE_COMMANDS.MEASURE_DELAY);
159 | context.sendBroadcast(get_delay_intent);
160 | }
161 |
162 | public static long getV2rayServerDelay(final String config) {
163 | return V2rayCoreExecutor.getConfigDelay(Utilities.normalizeV2rayFullConfig(config));
164 | }
165 |
166 | public static String getCoreVersion() {
167 | return Libv2ray.checkVersionX();
168 | }
169 |
170 | public static void toggleConnectionMode() {
171 | if (serviceMode == V2rayConstants.SERVICE_MODES.PROXY_MODE) {
172 | serviceMode = V2rayConstants.SERVICE_MODES.VPN_MODE;
173 | } else {
174 | serviceMode = V2rayConstants.SERVICE_MODES.PROXY_MODE;
175 | }
176 | }
177 |
178 | public static void toggleTrafficStatics() {
179 | if (currentConfig.enableTrafficStatics) {
180 | currentConfig.enableTrafficStatics = false;
181 | currentConfig.enableTrafficStaticsOnNotification = false;
182 | } else {
183 | currentConfig.enableTrafficStatics = true;
184 | currentConfig.enableTrafficStaticsOnNotification = true;
185 | }
186 | }
187 |
188 | private static void startTunnel(final Context context) {
189 | Intent start_intent;
190 | if (serviceMode == V2rayConstants.SERVICE_MODES.PROXY_MODE) {
191 | start_intent = new Intent(context, V2rayProxyService.class);
192 | } else {
193 | start_intent = new Intent(context, V2rayVPNService.class);
194 | }
195 | start_intent.setPackage(context.getPackageName());
196 | start_intent.putExtra(V2RAY_SERVICE_COMMAND_EXTRA, V2rayConstants.SERVICE_COMMANDS.START_SERVICE);
197 | start_intent.putExtra(V2RAY_SERVICE_CONFIG_EXTRA, currentConfig);
198 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
199 | context.startForegroundService(start_intent);
200 | } else {
201 | context.startService(start_intent);
202 | }
203 | }
204 |
205 | @Deprecated
206 | public static boolean IsPreparedForConnection(final Context context) {
207 | if (Build.VERSION.SDK_INT >= 33) {
208 | if (ContextCompat.checkSelfPermission(context, POST_NOTIFICATIONS) != PermissionChecker.PERMISSION_GRANTED) {
209 | return false;
210 | }
211 | }
212 | Intent vpnServicePrepareIntent = VpnService.prepare(context);
213 | return vpnServicePrepareIntent == null;
214 | }
215 |
216 | @Deprecated
217 | public static void StartV2ray(final Context context, final String remark, final String config, final ArrayList blocked_apps) {
218 | if (!Utilities.refillV2rayConfig(remark, config, blocked_apps)) {
219 | return;
220 | }
221 | startTunnel(context);
222 | }
223 |
224 | @Deprecated
225 | public static void StopV2ray(final Context context) {
226 | stopV2ray(context);
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/core/Tun2SocksExecutor.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.core;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.File;
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.Scanner;
9 |
10 | import dev.dev7.lib.v2ray.interfaces.Tun2SocksListener;
11 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
12 |
13 |
14 | public class Tun2SocksExecutor {
15 | private final Tun2SocksListener tun2SocksListener;
16 | private Process tun2SocksProcess;
17 |
18 | public Tun2SocksExecutor(final Tun2SocksListener tun2SocksListener) {
19 | this.tun2SocksListener = tun2SocksListener;
20 | }
21 |
22 | public void stopTun2Socks() {
23 | try {
24 | if (tun2SocksProcess != null) {
25 | tun2SocksProcess.destroy();
26 | tun2SocksProcess = null;
27 | }
28 | } catch (Exception ignore) {}
29 | tun2SocksListener.OnTun2SocksHasMassage(V2rayConstants.CORE_STATES.STOPPED, "T2S -> Tun2Socks Stopped.");
30 | }
31 |
32 | public boolean isTun2SucksRunning() {
33 | return tun2SocksProcess != null;
34 | }
35 |
36 | public void run(final Context context, final int socksPort, final int localDnsPort) {
37 | ArrayList tun2SocksCommands = new ArrayList<>(Arrays.asList(new File(context.getApplicationInfo().nativeLibraryDir, "libtun2socks.so").getAbsolutePath(),
38 | "--netif-ipaddr", "26.26.26.2",
39 | "--netif-netmask", "255.255.255.252",
40 | "--socks-server-addr", "127.0.0.1:" + socksPort,
41 | "--tunmtu", "1500",
42 | "--sock-path", "sock_path",
43 | "--enable-udprelay",
44 | "--loglevel", "error"));
45 | if (localDnsPort > 0 && localDnsPort < 65000) {
46 | tun2SocksCommands.add("--dnsgw");
47 | tun2SocksCommands.add("127.0.0.1:" + localDnsPort);
48 | }
49 | tun2SocksListener.OnTun2SocksHasMassage(V2rayConstants.CORE_STATES.IDLE, "T2S Start Commands => " + tun2SocksCommands);
50 | try {
51 | ProcessBuilder processBuilder = new ProcessBuilder(tun2SocksCommands);
52 | tun2SocksProcess = processBuilder.directory(context.getApplicationContext().getFilesDir()).start();
53 | new Thread(() -> {
54 | Scanner scanner = new Scanner(tun2SocksProcess.getInputStream());
55 | while (scanner.hasNextLine()) {
56 | String line = scanner.nextLine();
57 | tun2SocksListener.OnTun2SocksHasMassage(V2rayConstants.CORE_STATES.RUNNING, "T2S -> " + line);
58 | }
59 | }, "t2s_output_thread").start();
60 | new Thread(() -> {
61 | Scanner scanner = new Scanner(tun2SocksProcess.getErrorStream());
62 | while (scanner.hasNextLine()) {
63 | String line = scanner.nextLine();
64 | tun2SocksListener.OnTun2SocksHasMassage(V2rayConstants.CORE_STATES.RUNNING, "T2S => " + line);
65 | }
66 | }, "t2s_error_thread").start();
67 | new Thread(() -> {
68 | try {
69 | tun2SocksProcess.waitFor();
70 | } catch (InterruptedException e) {
71 | tun2SocksListener.OnTun2SocksHasMassage(V2rayConstants.CORE_STATES.STOPPED, "T2S -> Tun2socks Interrupted!" + e);
72 | tun2SocksProcess.destroy();
73 | tun2SocksProcess = null;
74 | }
75 | }, "t2s_main_thread").start();
76 | } catch (Exception e) {
77 | tun2SocksListener.OnTun2SocksHasMassage(V2rayConstants.CORE_STATES.IDLE, "Tun2socks Run Error =>> " + e);
78 | tun2SocksProcess.destroy();
79 | tun2SocksProcess = null;
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/core/V2rayCoreExecutor.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.core;
2 |
3 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_EXTRA;
4 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_INTENT;
5 | import static dev.dev7.lib.v2ray.utils.Utilities.getDeviceIdForXUDPBaseKey;
6 | import static dev.dev7.lib.v2ray.utils.Utilities.getUserAssetsPath;
7 |
8 | import android.app.Service;
9 | import android.content.Intent;
10 | import android.os.Build;
11 | import android.util.Log;
12 |
13 | import org.json.JSONObject;
14 |
15 | import dev.dev7.lib.v2ray.interfaces.V2rayServicesListener;
16 | import dev.dev7.lib.v2ray.model.V2rayConfigModel;
17 | import dev.dev7.lib.v2ray.utils.Utilities;
18 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
19 | import go.Seq;
20 | import libv2ray.Libv2ray;
21 | import libv2ray.V2RayPoint;
22 | import libv2ray.V2RayVPNServiceSupportsSet;
23 |
24 | public class V2rayCoreExecutor {
25 |
26 | private V2rayConstants.CORE_STATES coreState;
27 | public V2rayServicesListener v2rayServicesListener;
28 |
29 |
30 | public final V2RayPoint v2RayPoint = Libv2ray.newV2RayPoint(new V2RayVPNServiceSupportsSet() {
31 | @Override
32 | public long shutdown() {
33 | try {
34 | if (v2rayServicesListener == null) {
35 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "shutdown => can`t find initialed service.");
36 | return -1;
37 | }
38 | v2rayServicesListener.stopService();
39 | v2rayServicesListener = null;
40 | return 0;
41 | } catch (Exception e) {
42 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "shutdown =>", e);
43 | return -1;
44 | }
45 | }
46 |
47 | @Override
48 | public long prepare() {
49 | return 0;
50 | }
51 |
52 | @Override
53 | public boolean protect(long l) {
54 | if (v2rayServicesListener != null)
55 | return v2rayServicesListener.onProtect((int) l);
56 | return true;
57 | }
58 |
59 | @Override
60 | public long onEmitStatus(long l, String s) {
61 | return 0;
62 | }
63 |
64 | @Override
65 | public long setup(String s) {
66 | if (v2rayServicesListener != null) {
67 | try {
68 | coreState = V2rayConstants.CORE_STATES.RUNNING;
69 | v2rayServicesListener.startService();
70 | } catch (Exception e) {
71 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "setupFailed => ", e);
72 | return -1;
73 | }
74 | }
75 | return 0;
76 | }
77 | }, Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1);
78 |
79 |
80 | public V2rayCoreExecutor(final Service targetService) {
81 | this.v2rayServicesListener = (V2rayServicesListener) targetService;
82 | Seq.setContext(targetService);
83 | Libv2ray.initV2Env(getUserAssetsPath(targetService.getApplicationContext()), getDeviceIdForXUDPBaseKey());
84 | coreState = V2rayConstants.CORE_STATES.IDLE;
85 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "V2rayCoreExecutor -> New initialize from : " + targetService.getClass().getSimpleName());
86 | }
87 |
88 | public void startCore(final V2rayConfigModel v2rayConfig) {
89 | try {
90 | stopCore(false);
91 | try {
92 | Libv2ray.testConfig(v2rayConfig.fullJsonConfig);
93 | } catch (Exception testException) {
94 | coreState = V2rayConstants.CORE_STATES.STOPPED;
95 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "startCore => v2ray json config not valid.", testException);
96 | stopCore(true);
97 | return;
98 | }
99 | v2RayPoint.setConfigureFileContent(v2rayConfig.fullJsonConfig);
100 | v2RayPoint.setDomainName(Utilities.normalizeIpv6(v2rayConfig.currentServerAddress) + ":" + v2rayConfig.currentServerPort);
101 | v2RayPoint.runLoop(false);
102 | } catch (Exception e) {
103 | Log.e(V2rayCoreExecutor.class.getSimpleName(), "startCore =>", e);
104 | }
105 | }
106 |
107 | public void stopCore(final boolean shouldStopService) {
108 | try {
109 | if (v2RayPoint.getIsRunning()) {
110 | v2RayPoint.stopLoop();
111 | if (shouldStopService) {
112 | v2rayServicesListener.stopService();
113 | }
114 | coreState = V2rayConstants.CORE_STATES.STOPPED;
115 | }
116 | } catch (Exception e) {
117 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "stopCore =>", e);
118 | }
119 | }
120 |
121 | public long getDownloadSpeed() {
122 | return v2RayPoint.queryStats("block", "downlink") + v2RayPoint.queryStats("proxy", "downlink");
123 | }
124 |
125 | public long getUploadSpeed() {
126 | return v2RayPoint.queryStats("block", "uplink") + v2RayPoint.queryStats("proxy", "uplink");
127 | }
128 |
129 | public V2rayConstants.CORE_STATES getCoreState() {
130 | if (coreState == V2rayConstants.CORE_STATES.RUNNING) {
131 | if (!v2RayPoint.getIsRunning()) {
132 | coreState = V2rayConstants.CORE_STATES.STOPPED;
133 | }
134 | return coreState;
135 | }
136 | return coreState;
137 | }
138 |
139 | public void broadCastCurrentServerDelay() {
140 | try {
141 | if (v2rayServicesListener != null) {
142 | int serverDelay = (int) v2RayPoint.measureDelay("");
143 | Intent serverDelayBroadcast = new Intent(V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_INTENT);
144 | serverDelayBroadcast.setPackage(v2rayServicesListener.getService().getPackageName());
145 | serverDelayBroadcast.putExtra(V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_EXTRA, serverDelay);
146 | v2rayServicesListener.getService().sendBroadcast(serverDelayBroadcast);
147 | }
148 | } catch (Exception e) {
149 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "broadCastCurrentServerDelay => ", e);
150 | Intent serverDelayBroadcast = new Intent(V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_INTENT);
151 | serverDelayBroadcast.setPackage(v2rayServicesListener.getService().getPackageName());
152 | serverDelayBroadcast.putExtra(V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_EXTRA, -1);
153 | v2rayServicesListener.getService().sendBroadcast(serverDelayBroadcast);
154 | }
155 | }
156 |
157 | public static long getConfigDelay(final String config) {
158 | try {
159 | JSONObject config_json = new JSONObject(config);
160 | config_json.remove("routing");
161 | config_json.remove("dns");
162 | JSONObject routing = new JSONObject();
163 | routing.put("domainStrategy", "IPIfNonMatch");
164 | config_json.put("routing", routing);
165 | config_json.put("dns", new JSONObject("{\n" +
166 | " \"hosts\": {\n" +
167 | " \"domain:googleapis.cn\": \"googleapis.com\"\n" +
168 | " },\n" +
169 | " \"servers\": [\n" +
170 | " \"1.1.1.1\"\n" +
171 | " ]\n" +
172 | "}"));
173 | return Libv2ray.measureOutboundDelay(config_json.toString(), "");
174 | } catch (Exception json_error) {
175 | Log.d(V2rayCoreExecutor.class.getSimpleName(), "getCurrentServerDelay -> ", json_error);
176 | return -1;
177 | }
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/interfaces/LatencyDelayListener.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.interfaces;
2 |
3 | public interface LatencyDelayListener {
4 | void OnResultReady(long delayResult);
5 | }
6 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/interfaces/StateListener.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.interfaces;
2 |
3 |
4 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
5 |
6 | public interface StateListener {
7 | V2rayConstants.CONNECTION_STATES getConnectionState();
8 |
9 | V2rayConstants.CORE_STATES getCoreState();
10 |
11 | long getDownloadSpeed();
12 |
13 | long getUploadSpeed();
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/interfaces/TrafficListener.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.interfaces;
2 |
3 | public interface TrafficListener {
4 | void onTrafficChanged(long uploadSpeed, long downloadSpeed, long uploadedTraffic, long downloadedTraffic);
5 | }
6 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/interfaces/Tun2SocksListener.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.interfaces;
2 |
3 |
4 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
5 |
6 | public interface Tun2SocksListener {
7 | void OnTun2SocksHasMassage(V2rayConstants.CORE_STATES tun2SocksState, String newMessage);
8 | }
9 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/interfaces/V2rayServicesListener.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.interfaces;
2 |
3 | import android.app.Service;
4 |
5 | public interface V2rayServicesListener {
6 | boolean onProtect(final int socket);
7 | Service getService();
8 | void startService();
9 | void stopService();
10 | }
11 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/model/V2rayConfigModel.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.model;
2 |
3 | import java.io.Serializable;
4 | import java.util.ArrayList;
5 |
6 | public class V2rayConfigModel implements Serializable {
7 |
8 | public String applicationName;
9 | public int applicationIcon;
10 | public String remark;
11 | public ArrayList blockedApplications = null;
12 | public String fullJsonConfig;
13 | public String currentServerAddress = "";
14 | public int currentServerPort = 443;
15 | public int localSocksPort = 10808;
16 | public int localHttpPort = 10809;
17 | public int localDNSPort = 1053;
18 | public boolean enableTrafficStatics = true;
19 | public boolean enableTrafficStaticsOnNotification = true;
20 | public boolean enableLocalTunneledDNS = false;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/services/NotificationService.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.services;
2 |
3 | import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE;
4 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_OPENED_APPLICATION_INTENT;
5 |
6 | import android.annotation.SuppressLint;
7 | import android.app.Notification;
8 | import android.app.NotificationChannel;
9 | import android.app.NotificationManager;
10 | import android.app.PendingIntent;
11 | import android.app.Service;
12 | import android.content.Context;
13 | import android.content.Intent;
14 | import android.content.pm.ApplicationInfo;
15 | import android.content.pm.PackageManager;
16 | import android.graphics.Color;
17 | import android.os.Build;
18 |
19 | import androidx.annotation.RequiresApi;
20 | import androidx.core.app.NotificationCompat;
21 |
22 | import java.util.Objects;
23 |
24 | import dev.dev7.lib.v2ray.interfaces.TrafficListener;
25 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
26 | import dev.dev7.lib.v2ray.utils.Utilities;
27 |
28 | public class NotificationService {
29 | private NotificationManager mNotificationManager = null;
30 | private final NotificationCompat.Builder notifcationBuilder;
31 | public boolean isNotificationOnGoing;
32 | private final int NOTIFICATION_ID = 1;
33 |
34 | public TrafficListener trafficListener = new TrafficListener() {
35 | @Override
36 | public void onTrafficChanged(long uploadSpeed, long downloadSpeed, long uploadedTraffic, long downloadedTraffic) {
37 | if (mNotificationManager != null && notifcationBuilder != null) {
38 | if (isNotificationOnGoing) {
39 | notifcationBuilder.setSubText("Traffic ↓" + Utilities.parseTraffic(downloadedTraffic, false, false) + " ↑" + Utilities.parseTraffic(uploadedTraffic, false, false));
40 | notifcationBuilder.setContentText("Tap to open application.\n Download : ↓" + Utilities.parseTraffic(downloadSpeed, false, true) + " | Upload : ↑" + Utilities.parseTraffic(uploadSpeed, false, true));
41 | mNotificationManager.notify(NOTIFICATION_ID, notifcationBuilder.build());
42 | }
43 | }
44 | }
45 | };
46 |
47 |
48 | public NotificationService(final Service targetService) {
49 | Intent launchIntent = targetService.getPackageManager().
50 | getLaunchIntentForPackage(targetService.getApplicationInfo().packageName);
51 | assert launchIntent != null;
52 | launchIntent.setAction(V2RAY_SERVICE_OPENED_APPLICATION_INTENT);
53 | launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
54 | PendingIntent notificationContentPendingIntent = PendingIntent.getActivity(targetService, 0, launchIntent, judgeForNotificationFlag());
55 | String notificationChannelID = "";
56 | String applicationName = "unknown_name";
57 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
58 | try {
59 | PackageManager pm = targetService.getApplicationContext().getPackageManager();
60 | ApplicationInfo ai;
61 | ai = pm.getApplicationInfo(targetService.getPackageName(), 0);
62 | applicationName = (String) pm.getApplicationLabel(ai);
63 | notificationChannelID = createNotificationChannelID(targetService, applicationName);
64 | } catch (Exception e) {
65 | notificationChannelID = createNotificationChannelID(targetService, applicationName);
66 | }
67 | }
68 | Intent disconnectIntent = new Intent(targetService, targetService.getClass());
69 | disconnectIntent.setPackage(targetService.getPackageName());
70 | disconnectIntent.putExtra(V2rayConstants.V2RAY_SERVICE_COMMAND_EXTRA, V2rayConstants.SERVICE_COMMANDS.STOP_SERVICE);
71 | PendingIntent disconnectPendingIntent = PendingIntent.getService(targetService, 0, disconnectIntent, judgeForNotificationFlag());
72 | notifcationBuilder = new NotificationCompat.Builder(targetService, notificationChannelID);
73 | notifcationBuilder.setContentTitle(applicationName + " Connecting...")
74 | .setSmallIcon(android.R.drawable.sym_def_app_icon)
75 | .setContentText("Connecting on process.\nTap to open application")
76 | .setOngoing(true)
77 | .setShowWhen(false)
78 | .setOnlyAlertOnce(true)
79 | .setContentIntent(notificationContentPendingIntent)
80 | .setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE)
81 | .addAction(android.R.drawable.btn_minus, "Disconnect", disconnectPendingIntent);
82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
83 | notifcationBuilder.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE);
84 | } else {
85 | notifcationBuilder.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_DEFAULT);
86 | }
87 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
88 | targetService.startForeground(NOTIFICATION_ID, notifcationBuilder.build(), FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
89 | } else {
90 | targetService.startForeground(NOTIFICATION_ID, notifcationBuilder.build());
91 | }
92 | isNotificationOnGoing = true;
93 | }
94 |
95 | private NotificationManager getNotificationManager(final Service targetService) {
96 | if (mNotificationManager == null) {
97 | try {
98 | mNotificationManager = (NotificationManager) targetService.getSystemService(Context.NOTIFICATION_SERVICE);
99 | } catch (Exception e) {
100 | return null;
101 | }
102 | }
103 | return mNotificationManager;
104 | }
105 |
106 | public void setConnectedNotification(String remark, int iconResource) {
107 | if (mNotificationManager != null && notifcationBuilder != null) {
108 | if (isNotificationOnGoing) {
109 | notifcationBuilder.setSmallIcon(iconResource);
110 | notifcationBuilder.setContentTitle("Connected to " + remark);
111 | notifcationBuilder.setContentText("Application connected successfully.\nTap to open Application.");
112 | mNotificationManager.notify(NOTIFICATION_ID, notifcationBuilder.build());
113 | }
114 | }
115 | }
116 |
117 | @RequiresApi(api = Build.VERSION_CODES.O)
118 | private String createNotificationChannelID(final Service targetService, final String Application_name) {
119 | String notification_channel_id = "DEV7DEV_AXL_CH_ID";
120 | NotificationChannel notificationChannel = new NotificationChannel(
121 | notification_channel_id, Application_name + " Background Service", NotificationManager.IMPORTANCE_HIGH);
122 | notificationChannel.setLightColor(Color.BLUE);
123 | notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
124 | notificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
125 | Objects.requireNonNull(getNotificationManager(targetService)).createNotificationChannel(notificationChannel);
126 | return notification_channel_id;
127 | }
128 |
129 | @SuppressLint("ObsoleteSdkInt")
130 | private int judgeForNotificationFlag() {
131 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
132 | return PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT;
133 | } else {
134 | return PendingIntent.FLAG_UPDATE_CURRENT;
135 | }
136 | }
137 |
138 | public void dismissNotification() {
139 | if (mNotificationManager != null) {
140 | isNotificationOnGoing = false;
141 | mNotificationManager.cancel(NOTIFICATION_ID);
142 | }
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/services/StaticsBroadCastService.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.services;
2 |
3 |
4 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_CONNECTION_STATE_BROADCAST_EXTRA;
5 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_CORE_STATE_BROADCAST_EXTRA;
6 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_DOWNLOAD_SPEED_BROADCAST_EXTRA;
7 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_DOWNLOAD_TRAFFIC_BROADCAST_EXTRA;
8 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_DURATION_BROADCAST_EXTRA;
9 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_STATICS_BROADCAST_INTENT;
10 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_TYPE_BROADCAST_EXTRA;
11 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_UPLOAD_SPEED_BROADCAST_EXTRA;
12 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.SERVICE_UPLOAD_TRAFFIC_BROADCAST_EXTRA;
13 |
14 | import android.annotation.SuppressLint;
15 | import android.content.Context;
16 | import android.content.Intent;
17 | import android.os.Build;
18 | import android.os.CountDownTimer;
19 |
20 | import androidx.annotation.RequiresApi;
21 |
22 | import dev.dev7.lib.v2ray.interfaces.StateListener;
23 | import dev.dev7.lib.v2ray.interfaces.TrafficListener;
24 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
25 | import dev.dev7.lib.v2ray.utils.Utilities;
26 |
27 |
28 | public class StaticsBroadCastService {
29 | private String SERVICE_DURATION = "00:00:00";
30 | private int seconds, minutes, hours;
31 | private long totalDownload, totalUpload, uploadSpeed, downloadSpeed;
32 | private final CountDownTimer countDownTimer;
33 | public boolean isTrafficStaticsEnabled;
34 | public boolean isCounterStarted;
35 | public TrafficListener trafficListener;
36 |
37 | public StaticsBroadCastService(final Context targetService, final StateListener stateListener) {
38 | resetCounter();
39 | isCounterStarted = false;
40 | countDownTimer = new CountDownTimer(604800000, 1000) {
41 | @SuppressLint("ObsoleteSdkInt")
42 | @RequiresApi(api = Build.VERSION_CODES.M)
43 | public void onTick(long millisUntilFinished) {
44 | seconds++;
45 | if (seconds == 59) {
46 | minutes++;
47 | seconds = 0;
48 | }
49 | if (minutes == 59) {
50 | minutes = 0;
51 | hours++;
52 | }
53 | if (hours == 23) {
54 | hours = 0;
55 | }
56 | if (isTrafficStaticsEnabled) {
57 | downloadSpeed = stateListener.getDownloadSpeed();
58 | uploadSpeed = stateListener.getUploadSpeed();
59 | totalDownload = totalDownload + downloadSpeed;
60 | totalUpload = totalUpload + uploadSpeed;
61 | if (trafficListener != null) {
62 | trafficListener.onTrafficChanged(uploadSpeed, downloadSpeed, totalUpload, totalDownload);
63 | }
64 | }
65 | SERVICE_DURATION = Utilities.convertIntToTwoDigit(hours) + ":" + Utilities.convertIntToTwoDigit(minutes) + ":" + Utilities.convertIntToTwoDigit(seconds);
66 | sendBroadCast(targetService, stateListener);
67 | }
68 |
69 | public void onFinish() {
70 | countDownTimer.cancel();
71 | new StaticsBroadCastService(targetService, stateListener).start();
72 | }
73 | };
74 | }
75 |
76 | public void resetCounter() {
77 | SERVICE_DURATION = "00:00:00";
78 | seconds = 0;
79 | minutes = 0;
80 | hours = 0;
81 | uploadSpeed = 0;
82 | downloadSpeed = 0;
83 | }
84 |
85 | public void start() {
86 | if (!isCounterStarted) {
87 | countDownTimer.start();
88 | isCounterStarted = true;
89 | }
90 | }
91 |
92 | public void stop() {
93 | if (isCounterStarted) {
94 | isCounterStarted = false;
95 | countDownTimer.cancel();
96 | }
97 | }
98 |
99 | public void sendBroadCast(final Context targetService, final StateListener stateListener) {
100 | Intent connection_info_intent = new Intent(V2RAY_SERVICE_STATICS_BROADCAST_INTENT);
101 | connection_info_intent.setPackage(targetService.getPackageName());
102 | connection_info_intent.putExtra(SERVICE_CONNECTION_STATE_BROADCAST_EXTRA, stateListener.getConnectionState());
103 | connection_info_intent.putExtra(SERVICE_CORE_STATE_BROADCAST_EXTRA, stateListener.getCoreState());
104 | connection_info_intent.putExtra(SERVICE_DURATION_BROADCAST_EXTRA, SERVICE_DURATION);
105 | connection_info_intent.putExtra(SERVICE_TYPE_BROADCAST_EXTRA, targetService.getClass().getSimpleName());
106 | connection_info_intent.putExtra(SERVICE_UPLOAD_SPEED_BROADCAST_EXTRA, Utilities.parseTraffic(uploadSpeed, false, true));
107 | connection_info_intent.putExtra(SERVICE_DOWNLOAD_SPEED_BROADCAST_EXTRA, Utilities.parseTraffic(downloadSpeed, false, true));
108 | connection_info_intent.putExtra(SERVICE_UPLOAD_TRAFFIC_BROADCAST_EXTRA, Utilities.parseTraffic(totalUpload, false, false));
109 | connection_info_intent.putExtra(SERVICE_DOWNLOAD_TRAFFIC_BROADCAST_EXTRA, Utilities.parseTraffic(totalDownload, false, false));
110 | targetService.sendBroadcast(connection_info_intent);
111 | }
112 |
113 | public void sendDisconnectedBroadCast(final Context targetService) {
114 | resetCounter();
115 | Intent connection_info_intent = new Intent(V2RAY_SERVICE_STATICS_BROADCAST_INTENT);
116 | connection_info_intent.setPackage(targetService.getPackageName());
117 | connection_info_intent.putExtra(SERVICE_CONNECTION_STATE_BROADCAST_EXTRA, V2rayConstants.CONNECTION_STATES.DISCONNECTED);
118 | connection_info_intent.putExtra(SERVICE_CORE_STATE_BROADCAST_EXTRA, V2rayConstants.CORE_STATES.STOPPED);
119 | connection_info_intent.putExtra(SERVICE_TYPE_BROADCAST_EXTRA, targetService.getClass().getSimpleName());
120 | connection_info_intent.putExtra(SERVICE_DURATION_BROADCAST_EXTRA, "00:00:00");
121 | connection_info_intent.putExtra(SERVICE_UPLOAD_SPEED_BROADCAST_EXTRA, "0.0 B/s");
122 | connection_info_intent.putExtra(SERVICE_DOWNLOAD_SPEED_BROADCAST_EXTRA, "0.0 B/s");
123 | connection_info_intent.putExtra(SERVICE_UPLOAD_TRAFFIC_BROADCAST_EXTRA, "0.0 B");
124 | connection_info_intent.putExtra(SERVICE_DOWNLOAD_TRAFFIC_BROADCAST_EXTRA, "0.0 B");
125 | targetService.sendBroadcast(connection_info_intent);
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/services/V2rayProxyService.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.services;
2 |
3 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_COMMAND_INTENT;
4 |
5 | import android.annotation.SuppressLint;
6 | import android.app.Service;
7 | import android.content.BroadcastReceiver;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.content.IntentFilter;
11 | import android.os.Build;
12 | import android.os.IBinder;
13 | import android.util.Log;
14 | import androidx.annotation.Nullable;
15 |
16 | import dev.dev7.lib.v2ray.core.V2rayCoreExecutor;
17 | import dev.dev7.lib.v2ray.interfaces.StateListener;
18 | import dev.dev7.lib.v2ray.interfaces.V2rayServicesListener;
19 | import dev.dev7.lib.v2ray.model.V2rayConfigModel;
20 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
21 |
22 | public class V2rayProxyService extends Service implements V2rayServicesListener {
23 | private V2rayCoreExecutor v2rayCoreExecutor;
24 | private NotificationService notificationService;
25 | private StaticsBroadCastService staticsBroadCastService;
26 | private V2rayConstants.CONNECTION_STATES connectionState = V2rayConstants.CONNECTION_STATES.DISCONNECTED;
27 | private V2rayConfigModel currentConfig = new V2rayConfigModel();
28 | private boolean isServiceCreated = false;
29 |
30 | private final BroadcastReceiver serviceCommandBroadcastReceiver = new BroadcastReceiver() {
31 | @Override
32 | public void onReceive(Context context, Intent intent) {
33 | try {
34 | V2rayConstants.SERVICE_COMMANDS serviceCommand = (V2rayConstants.SERVICE_COMMANDS) intent.getSerializableExtra(V2rayConstants.V2RAY_SERVICE_COMMAND_EXTRA);
35 | if (serviceCommand == null) {
36 | return;
37 | }
38 | switch (serviceCommand) {
39 | case STOP_SERVICE:
40 | if (v2rayCoreExecutor != null) {
41 | v2rayCoreExecutor.stopCore(true);
42 | }
43 | break;
44 | case MEASURE_DELAY:
45 | if (v2rayCoreExecutor != null) {
46 | v2rayCoreExecutor.broadCastCurrentServerDelay();
47 | }
48 | break;
49 | default:
50 | break;
51 | }
52 | }catch (Exception ignore){}
53 | }
54 | };
55 |
56 | @Override
57 | public void onCreate() {
58 | super.onCreate();
59 | if (!isServiceCreated) {
60 | connectionState = V2rayConstants.CONNECTION_STATES.CONNECTING;
61 | v2rayCoreExecutor = new V2rayCoreExecutor(this);
62 | notificationService = new NotificationService(this);
63 | staticsBroadCastService = new StaticsBroadCastService(this, new StateListener() {
64 | @Override
65 | public V2rayConstants.CONNECTION_STATES getConnectionState() {
66 | return connectionState;
67 | }
68 |
69 | @Override
70 | public V2rayConstants.CORE_STATES getCoreState() {
71 | if (v2rayCoreExecutor == null) {
72 | return V2rayConstants.CORE_STATES.IDLE;
73 | }
74 | return v2rayCoreExecutor.getCoreState();
75 | }
76 |
77 | @Override
78 | public long getDownloadSpeed() {
79 | if (v2rayCoreExecutor == null) {
80 | return -1;
81 | }
82 | return v2rayCoreExecutor.getDownloadSpeed();
83 | }
84 |
85 | @Override
86 | public long getUploadSpeed() {
87 | if (v2rayCoreExecutor == null) {
88 | return -1;
89 | }
90 | return v2rayCoreExecutor.getUploadSpeed();
91 | }
92 | });
93 | isServiceCreated = true;
94 | }
95 | }
96 |
97 | @SuppressLint("UnspecifiedRegisterReceiverFlag")
98 | @Override
99 | public int onStartCommand(Intent intent, int flags, int startId) {
100 | try {
101 | V2rayConstants.SERVICE_COMMANDS serviceCommand = (V2rayConstants.SERVICE_COMMANDS) intent.getSerializableExtra(V2rayConstants.V2RAY_SERVICE_COMMAND_EXTRA);
102 | if (serviceCommand == null) {
103 | return super.onStartCommand(intent, flags, startId);
104 | }
105 | switch (serviceCommand) {
106 | case STOP_SERVICE:
107 | v2rayCoreExecutor.stopCore(true);
108 | break;
109 | case START_SERVICE:
110 | currentConfig = (V2rayConfigModel) intent.getSerializableExtra(V2rayConstants.V2RAY_SERVICE_CONFIG_EXTRA);
111 | if (currentConfig == null) {
112 | stopService();
113 | break;
114 | }
115 | staticsBroadCastService.isTrafficStaticsEnabled = currentConfig.enableTrafficStatics;
116 | if (currentConfig.enableTrafficStatics && currentConfig.enableTrafficStaticsOnNotification) {
117 | staticsBroadCastService.trafficListener = notificationService.trafficListener;
118 | }
119 | v2rayCoreExecutor.startCore(currentConfig);
120 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
121 | registerReceiver(serviceCommandBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_COMMAND_INTENT), RECEIVER_EXPORTED);
122 | } else {
123 | registerReceiver(serviceCommandBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_COMMAND_INTENT));
124 | }
125 | return START_STICKY;
126 | default:
127 | onDestroy();
128 | break;
129 | }
130 | }catch (Exception ignore){}
131 | return super.onStartCommand(intent, flags, startId);
132 | }
133 |
134 | @Nullable
135 | @Override
136 | public IBinder onBind(Intent intent) {
137 | return null;
138 | }
139 |
140 | @Override
141 | public void onDestroy() {
142 | unregisterReceiver(serviceCommandBroadcastReceiver);
143 | super.onDestroy();
144 | }
145 |
146 | @Override
147 | public boolean onProtect(int socket) {
148 | return true;
149 | }
150 |
151 | @Override
152 | public Service getService() {
153 | return this;
154 | }
155 |
156 | @Override
157 | public void startService() {
158 | connectionState = V2rayConstants.CONNECTION_STATES.CONNECTED;
159 | notificationService.setConnectedNotification(currentConfig.remark, currentConfig.applicationIcon);
160 | staticsBroadCastService.start();
161 | }
162 |
163 | @Override
164 | public void stopService() {
165 | try {
166 | staticsBroadCastService.sendDisconnectedBroadCast(this);
167 | staticsBroadCastService.stop();
168 | notificationService.dismissNotification();
169 | stopForeground(true);
170 | stopSelf();
171 | } catch (Exception e) {
172 | Log.d(V2rayProxyService.class.getSimpleName(), "stopService => ", e);
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/services/V2rayVPNService.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.services;
2 |
3 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_COMMAND_EXTRA;
4 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.V2RAY_SERVICE_COMMAND_INTENT;
5 |
6 | import android.annotation.SuppressLint;
7 | import android.app.Service;
8 | import android.content.BroadcastReceiver;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.content.IntentFilter;
12 | import android.net.LocalSocket;
13 | import android.net.LocalSocketAddress;
14 | import android.net.VpnService;
15 | import android.os.Build;
16 | import android.os.ParcelFileDescriptor;
17 | import android.os.StrictMode;
18 | import android.util.Log;
19 |
20 | import androidx.annotation.NonNull;
21 |
22 | import java.io.File;
23 | import java.io.FileDescriptor;
24 | import java.io.OutputStream;
25 |
26 | import dev.dev7.lib.v2ray.core.Tun2SocksExecutor;
27 | import dev.dev7.lib.v2ray.core.V2rayCoreExecutor;
28 | import dev.dev7.lib.v2ray.interfaces.StateListener;
29 | import dev.dev7.lib.v2ray.interfaces.Tun2SocksListener;
30 | import dev.dev7.lib.v2ray.model.V2rayConfigModel;
31 | import dev.dev7.lib.v2ray.utils.V2rayConstants;
32 |
33 | import dev.dev7.lib.v2ray.interfaces.V2rayServicesListener;
34 |
35 | public class V2rayVPNService extends VpnService implements V2rayServicesListener, Tun2SocksListener {
36 | private ParcelFileDescriptor tunnelInterface;
37 | private Tun2SocksExecutor tun2SocksExecutor;
38 | private V2rayCoreExecutor v2rayCoreExecutor;
39 | private NotificationService notificationService;
40 | private StaticsBroadCastService staticsBroadCastService;
41 | private V2rayConstants.CONNECTION_STATES connectionState = V2rayConstants.CONNECTION_STATES.DISCONNECTED;
42 | private V2rayConfigModel currentConfig = new V2rayConfigModel();
43 | private boolean isServiceCreated = false;
44 | private boolean isServiceSetupStarted = false;
45 |
46 | private final BroadcastReceiver serviceCommandBroadcastReceiver = new BroadcastReceiver() {
47 | @Override
48 | public void onReceive(Context context, Intent intent) {
49 | try {
50 | V2rayConstants.SERVICE_COMMANDS serviceCommand = (V2rayConstants.SERVICE_COMMANDS) intent.getSerializableExtra(V2rayConstants.V2RAY_SERVICE_COMMAND_EXTRA);
51 | if (serviceCommand == null) {
52 | return;
53 | }
54 | switch (serviceCommand) {
55 | case STOP_SERVICE:
56 | if (v2rayCoreExecutor != null) {
57 | v2rayCoreExecutor.stopCore(true);
58 | }
59 | break;
60 | case MEASURE_DELAY:
61 | if (v2rayCoreExecutor != null) {
62 | v2rayCoreExecutor.broadCastCurrentServerDelay();
63 | }
64 | break;
65 | default:
66 | break;
67 | }
68 | } catch (Exception ignore) {
69 | }
70 | }
71 | };
72 |
73 |
74 | @Override
75 | public void onCreate() {
76 | super.onCreate();
77 | if (!isServiceCreated) {
78 | connectionState = V2rayConstants.CONNECTION_STATES.CONNECTING;
79 | StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
80 | StrictMode.setThreadPolicy(threadPolicy);
81 | tun2SocksExecutor = new Tun2SocksExecutor(this);
82 | v2rayCoreExecutor = new V2rayCoreExecutor(this);
83 | notificationService = new NotificationService(this);
84 | staticsBroadCastService = new StaticsBroadCastService(this, new StateListener() {
85 | @Override
86 | public V2rayConstants.CONNECTION_STATES getConnectionState() {
87 | return connectionState;
88 | }
89 |
90 | @Override
91 | public V2rayConstants.CORE_STATES getCoreState() {
92 | if (v2rayCoreExecutor == null) {
93 | return V2rayConstants.CORE_STATES.IDLE;
94 | }
95 | return v2rayCoreExecutor.getCoreState();
96 | }
97 |
98 | @Override
99 | public long getDownloadSpeed() {
100 | if (v2rayCoreExecutor == null) {
101 | return -1;
102 | }
103 | return v2rayCoreExecutor.getDownloadSpeed();
104 | }
105 |
106 | @Override
107 | public long getUploadSpeed() {
108 | if (v2rayCoreExecutor == null) {
109 | return -1;
110 | }
111 | return v2rayCoreExecutor.getUploadSpeed();
112 | }
113 |
114 |
115 | });
116 | isServiceCreated = true;
117 | }
118 | }
119 |
120 | @SuppressLint("UnspecifiedRegisterReceiverFlag")
121 | @Override
122 | public int onStartCommand(Intent intent, int flags, int startId) {
123 | try {
124 | V2rayConstants.SERVICE_COMMANDS serviceCommand = (V2rayConstants.SERVICE_COMMANDS) intent.getSerializableExtra(V2RAY_SERVICE_COMMAND_EXTRA);
125 | if (serviceCommand == null) {
126 | return super.onStartCommand(intent, flags, startId);
127 | }
128 | switch (serviceCommand) {
129 | case STOP_SERVICE:
130 | v2rayCoreExecutor.stopCore(true);
131 | break;
132 | case START_SERVICE:
133 | currentConfig = (V2rayConfigModel) intent.getSerializableExtra(V2rayConstants.V2RAY_SERVICE_CONFIG_EXTRA);
134 | if (currentConfig == null) {
135 | stopService();
136 | break;
137 | }
138 | staticsBroadCastService.isTrafficStaticsEnabled = currentConfig.enableTrafficStatics;
139 | if (currentConfig.enableTrafficStatics && currentConfig.enableTrafficStaticsOnNotification) {
140 | staticsBroadCastService.trafficListener = notificationService.trafficListener;
141 | }
142 | v2rayCoreExecutor.startCore(currentConfig);
143 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
144 | registerReceiver(serviceCommandBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_COMMAND_INTENT), RECEIVER_EXPORTED);
145 | } else {
146 | registerReceiver(serviceCommandBroadcastReceiver, new IntentFilter(V2RAY_SERVICE_COMMAND_INTENT));
147 | }
148 | return START_STICKY;
149 | default:
150 | onDestroy();
151 | break;
152 | }
153 | } catch (Exception ignore) {
154 | }
155 | return super.onStartCommand(intent, flags, startId);
156 | }
157 |
158 | @Override
159 | public void onRevoke() {
160 | stopService();
161 | }
162 |
163 | private void setupService() {
164 | if (isServiceSetupStarted) {
165 | return;
166 | } else {
167 | isServiceSetupStarted = true;
168 | }
169 | try {
170 | if (tunnelInterface != null) {
171 | tunnelInterface.close();
172 | }
173 | } catch (Exception ignore) {
174 | }
175 | Intent prepare_intent = prepare(this);
176 | if (prepare_intent != null) {
177 | return;
178 | }
179 | Builder builder = getBuilder();
180 | try {
181 | // builder.addDisallowedApplication(getPackageName());
182 | tunnelInterface = builder.establish();
183 | int localDNSPort = 0;
184 | if (currentConfig.enableLocalTunneledDNS){
185 | localDNSPort = currentConfig.localDNSPort;
186 | }
187 | tun2SocksExecutor.run(this, currentConfig.localSocksPort,localDNSPort );
188 | sendFileDescriptor();
189 | if (tun2SocksExecutor.isTun2SucksRunning()) {
190 | connectionState = V2rayConstants.CONNECTION_STATES.CONNECTED;
191 | notificationService.setConnectedNotification(currentConfig.remark, currentConfig.applicationIcon);
192 | staticsBroadCastService.start();
193 | }
194 | } catch (Exception e) {
195 | Log.e(V2rayVPNService.class.getSimpleName(), "setupFailed => ", e);
196 | stopService();
197 | }
198 | }
199 |
200 | @NonNull
201 | private Builder getBuilder() {
202 | Builder builder = new Builder();
203 | builder.setSession(currentConfig.remark);
204 | builder.setMtu(1500);
205 | builder.addAddress("26.26.26.1", 30);
206 | builder.addRoute("0.0.0.0", 0);
207 | if (currentConfig.enableLocalTunneledDNS) {
208 | builder.addDnsServer("26.26.26.2");
209 | } else {
210 | builder.addDnsServer("1.1.1.1");
211 | builder.addDnsServer("8.8.8.8");
212 | }
213 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
214 | builder.setMetered(false);
215 | }
216 | if (currentConfig.blockedApplications != null) {
217 | for (int i = 0; i < currentConfig.blockedApplications.size(); i++) {
218 | try {
219 | builder.addDisallowedApplication(currentConfig.blockedApplications.get(i));
220 | } catch (Exception ignore) {
221 | }
222 | }
223 | }
224 | return builder;
225 | }
226 |
227 | private void sendFileDescriptor() {
228 | String localSocksFile = new File(getApplicationContext().getFilesDir(), "sock_path").getAbsolutePath();
229 | FileDescriptor tunFd = tunnelInterface.getFileDescriptor();
230 | new Thread(() -> {
231 | boolean isSendFDSuccess = false;
232 | for (int sendFDTries = 0; sendFDTries < 5; sendFDTries++) {
233 | try {
234 | Thread.sleep(50L * sendFDTries);
235 | LocalSocket clientLocalSocket = new LocalSocket();
236 | clientLocalSocket.connect(new LocalSocketAddress(localSocksFile, LocalSocketAddress.Namespace.FILESYSTEM));
237 | if (!clientLocalSocket.isConnected()) {
238 | Log.i("SOCK_FILE", "Unable to connect to localSocksFile [" + localSocksFile + "]");
239 | } else {
240 | Log.i("SOCK_FILE", "connected to sock file [" + localSocksFile + "]");
241 | }
242 | OutputStream clientOutStream = clientLocalSocket.getOutputStream();
243 | clientLocalSocket.setFileDescriptorsForSend(new FileDescriptor[]{tunFd});
244 | clientOutStream.write(42);
245 | // clientLocalSocket.setFileDescriptorsForSend(null);
246 | clientLocalSocket.shutdownOutput();
247 | clientLocalSocket.close();
248 | isSendFDSuccess = true;
249 | break;
250 | } catch (Exception ignore) {
251 | }
252 | }
253 | if (!isSendFDSuccess) {
254 | Log.w("SendFDFailed", "Could`nt send file descriptor !");
255 | }
256 | }, "sendFd_Thread").start();
257 | }
258 |
259 | @Override
260 | public void onDestroy() {
261 | unregisterReceiver(serviceCommandBroadcastReceiver);
262 | super.onDestroy();
263 | }
264 |
265 | @Override
266 | public boolean onProtect(int socket) {
267 | return protect(socket);
268 | }
269 |
270 | @Override
271 | public Service getService() {
272 | return this;
273 | }
274 |
275 | @Override
276 | public void startService() {
277 | setupService();
278 | }
279 |
280 | @Override
281 | public void stopService() {
282 | try {
283 | staticsBroadCastService.sendDisconnectedBroadCast(this);
284 | tun2SocksExecutor.stopTun2Socks();
285 | staticsBroadCastService.stop();
286 | notificationService.dismissNotification();
287 | stopForeground(true);
288 | stopSelf();
289 | try {
290 | tunnelInterface.close();
291 | } catch (Exception ignore) {
292 |
293 | }
294 | } catch (Exception e) {
295 | Log.d(V2rayVPNService.class.getSimpleName(), "stopService => ", e);
296 | }
297 | }
298 |
299 | @Override
300 | public void OnTun2SocksHasMassage(V2rayConstants.CORE_STATES tun2SocksState, String newMessage) {
301 | Log.i("TUN2SOCKS", newMessage);
302 | }
303 |
304 | }
305 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/utils/Utilities.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.utils;
2 |
3 | import static dev.dev7.lib.v2ray.utils.V2rayConfigs.currentConfig;
4 | import static dev.dev7.lib.v2ray.utils.V2rayConstants.DEFAULT_OUT_BOUND_PLACE_IN_FULL_JSON_CONFIG;
5 |
6 | import android.content.Context;
7 | import android.provider.Settings;
8 | import android.util.Base64;
9 | import android.util.Log;
10 |
11 | import org.json.JSONArray;
12 | import org.json.JSONObject;
13 |
14 | import java.io.File;
15 | import java.io.FileOutputStream;
16 | import java.io.IOException;
17 | import java.io.InputStream;
18 | import java.io.OutputStream;
19 | import java.nio.charset.StandardCharsets;
20 | import java.nio.file.Files;
21 | import java.util.ArrayList;
22 | import java.util.Arrays;
23 | import java.util.Locale;
24 | import java.util.Objects;
25 |
26 | import libv2ray.Libv2ray;
27 |
28 | public class Utilities {
29 |
30 | public static String getDeviceIdForXUDPBaseKey() {
31 | String androidId = Settings.Secure.ANDROID_ID;
32 | byte[] androidIdBytes = androidId.getBytes(StandardCharsets.UTF_8);
33 | return Base64.encodeToString(Arrays.copyOf(androidIdBytes, 32), Base64.NO_PADDING | Base64.URL_SAFE);
34 | }
35 |
36 | public static void CopyFiles(InputStream src, File dst) throws IOException {
37 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
38 | try (OutputStream out = Files.newOutputStream(dst.toPath())) {
39 | byte[] buf = new byte[1024];
40 | int len;
41 | while ((len = src.read(buf)) > 0) {
42 | out.write(buf, 0, len);
43 | }
44 | }
45 | } else {
46 | try (OutputStream out = new FileOutputStream(dst)) {
47 | byte[] buf = new byte[1024];
48 | int len;
49 | while ((len = src.read(buf)) > 0) {
50 | out.write(buf, 0, len);
51 | }
52 | }
53 | }
54 | }
55 |
56 | public static String getUserAssetsPath(Context context) {
57 | File extDir = context.getExternalFilesDir("assets");
58 | if (extDir == null) {
59 | return "";
60 | }
61 | if (!extDir.exists()) {
62 | return context.getDir("assets", 0).getAbsolutePath();
63 | } else {
64 | return extDir.getAbsolutePath();
65 | }
66 | }
67 |
68 | public static void copyAssets(final Context context) {
69 | String extFolder = getUserAssetsPath(context);
70 | try {
71 | String geo = "geosite.dat,geoip.dat";
72 | for (String assets_obj : Objects.requireNonNull(context.getAssets().list(""))) {
73 | if (geo.contains(assets_obj)) {
74 | CopyFiles(context.getAssets().open(assets_obj), new File(extFolder, assets_obj));
75 | }
76 | }
77 | } catch (Exception e) {
78 | Log.e(Utilities.class.getSimpleName(), "copyAssets failed=>", e);
79 | }
80 | }
81 |
82 | public static String convertIntToTwoDigit(int value) {
83 | if (value < 10) return "0" + value;
84 | else return String.valueOf(value);
85 | }
86 |
87 | public static String parseTraffic(final double bytes, final boolean inBits, final boolean isMomentary) {
88 | double value = inBits ? bytes * 8 : bytes;
89 | if (value < V2rayConstants.KILO_BYTE) {
90 | return String.format(Locale.getDefault(), "%.1f " + (inBits ? "b" : "B") + (isMomentary ? "/s" : ""), value);
91 | } else if (value < V2rayConstants.MEGA_BYTE) {
92 | return String.format(Locale.getDefault(), "%.1f K" + (inBits ? "b" : "B") + (isMomentary ? "/s" : ""), value / V2rayConstants.KILO_BYTE);
93 | } else if (value < V2rayConstants.GIGA_BYTE) {
94 | return String.format(Locale.getDefault(), "%.1f M" + (inBits ? "b" : "B") + (isMomentary ? "/s" : ""), value / V2rayConstants.MEGA_BYTE);
95 | } else {
96 | return String.format(Locale.getDefault(), "%.2f G" + (inBits ? "b" : "B") + (isMomentary ? "/s" : ""), value / V2rayConstants.GIGA_BYTE);
97 | }
98 | }
99 |
100 | public static String normalizeV2rayFullConfig(String config){
101 | if (Libv2ray.isXrayURI(config)){
102 | return V2rayConstants.DEFAULT_FULL_JSON_CONFIG.replace(DEFAULT_OUT_BOUND_PLACE_IN_FULL_JSON_CONFIG,Libv2ray.getXrayOutboundFromURI(config));
103 | }
104 | return config;
105 | }
106 |
107 | public static boolean refillV2rayConfig(String remark, String config, final ArrayList blockedApplications) {
108 | currentConfig.remark = remark;
109 | currentConfig.blockedApplications = blockedApplications;
110 | try {
111 | JSONObject config_json = new JSONObject(normalizeV2rayFullConfig(config));
112 | try {
113 | JSONArray inbounds = config_json.getJSONArray("inbounds");
114 | for (int i = 0; i < inbounds.length(); i++) {
115 | try {
116 | if (inbounds.getJSONObject(i).getString("protocol").equals("socks")) {
117 | currentConfig.localSocksPort = inbounds.getJSONObject(i).getInt("port");
118 | }
119 | } catch (Exception e) {
120 | //ignore
121 | }
122 | try {
123 | if (inbounds.getJSONObject(i).getString("protocol").equals("http")) {
124 | currentConfig.localHttpPort = inbounds.getJSONObject(i).getInt("port");
125 | }
126 | } catch (Exception e) {
127 | //ignore
128 | }
129 | }
130 | } catch (Exception e) {
131 | Log.w(Utilities.class.getSimpleName(), "startCore warn => can`t find inbound port of socks5 or http.");
132 | return false;
133 | }
134 | try {
135 | currentConfig.currentServerAddress = config_json.getJSONArray("outbounds").getJSONObject(0).getJSONObject("settings").getJSONArray("vnext").getJSONObject(0).getString("address");
136 | currentConfig.currentServerPort = config_json.getJSONArray("outbounds").getJSONObject(0).getJSONObject("settings").getJSONArray("vnext").getJSONObject(0).getInt("port");
137 | } catch (Exception e) {
138 | currentConfig.currentServerAddress = config_json.getJSONArray("outbounds").getJSONObject(0).getJSONObject("settings").getJSONArray("servers").getJSONObject(0).getString("address");
139 | currentConfig.currentServerPort = config_json.getJSONArray("outbounds").getJSONObject(0).getJSONObject("settings").getJSONArray("servers").getJSONObject(0).getInt("port");
140 | }
141 | try {
142 | if (config_json.has("policy")) {
143 | config_json.remove("policy");
144 | }
145 | if (config_json.has("stats")) {
146 | config_json.remove("stats");
147 | }
148 | } catch (Exception ignore_error) {
149 | //ignore
150 | }
151 | if (currentConfig.enableTrafficStatics) {
152 | try {
153 | JSONObject policy = new JSONObject();
154 | JSONObject levels = new JSONObject();
155 | levels.put("8", new JSONObject().put("connIdle", 300).put("downlinkOnly", 1).put("handshake", 4).put("uplinkOnly", 1));
156 | JSONObject system = new JSONObject().put("statsOutboundUplink", true).put("statsOutboundDownlink", true);
157 | policy.put("levels", levels);
158 | policy.put("system", system);
159 | config_json.put("policy", policy);
160 | config_json.put("stats", new JSONObject());
161 | } catch (Exception e) {
162 | Log.e("log is here",e.toString());
163 | currentConfig.enableTrafficStatics = false;
164 | //ignore
165 | }
166 | }
167 | currentConfig.fullJsonConfig = config_json.toString();
168 | return true;
169 | } catch (Exception e) {
170 | Log.e(Utilities.class.getSimpleName(), "parseV2rayJsonFile failed => ", e);
171 | return false;
172 | }
173 | }
174 |
175 | public static String normalizeIpv6(String address) {
176 | if (isIpv6Address(address) && !address.contains("[") && !address.contains("]")) {
177 | return String.format("[%s]", address);
178 | } else {
179 | return address;
180 | }
181 | }
182 |
183 | public static boolean isIpv6Address(String address) {
184 | String[] tmp = address.split(":");
185 | return tmp.length > 2;
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/utils/V2rayConfigs.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.utils;
2 |
3 |
4 | import dev.dev7.lib.v2ray.model.V2rayConfigModel;
5 |
6 | public class V2rayConfigs {
7 | public static V2rayConstants.CONNECTION_STATES connectionState = V2rayConstants.CONNECTION_STATES.DISCONNECTED;
8 | public static V2rayConstants.SERVICE_MODES serviceMode = V2rayConstants.SERVICE_MODES.VPN_MODE;
9 | public static V2rayConfigModel currentConfig = new V2rayConfigModel();
10 | }
11 |
--------------------------------------------------------------------------------
/v2ray/src/main/java/dev/dev7/lib/v2ray/utils/V2rayConstants.java:
--------------------------------------------------------------------------------
1 | package dev.dev7.lib.v2ray.utils;
2 |
3 | public class V2rayConstants {
4 | public static final String V2RAY_SERVICE_OPENED_APPLICATION_INTENT = "APP_OPEN_FROM_V2RAY_NOTIFICATION_INTENT";
5 | public static final String V2RAY_SERVICE_STATICS_BROADCAST_INTENT = "V2RAY_SERVICE_STATICS_INTENT";
6 | public static final String V2RAY_SERVICE_COMMAND_INTENT = "V2RAY_SERVICE_COMMAND_INTENT";
7 | public static final String V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_INTENT = "V2RAY_SERVICE_CURRENT_CONFIG_DELAY_INTENT";
8 | public static final String V2RAY_SERVICE_CURRENT_CONFIG_DELAY_BROADCAST_EXTRA = "V2RAY_SERVICE_CURRENT_CONFIG_DELAY_EXTRA";
9 | public static final String V2RAY_SERVICE_COMMAND_EXTRA = "V2RAY_SERVICE_COMMAND_EXTRA";
10 | public static final String V2RAY_SERVICE_CONFIG_EXTRA = "V2RAY_SERVICE_CONFIG_EXTRA";
11 | public static final String SERVICE_CONNECTION_STATE_BROADCAST_EXTRA = "CONNECTION_STATE_EXTRA";
12 | public static final String SERVICE_TYPE_BROADCAST_EXTRA = "SERVICE_TYPE_EXTRA";
13 | public static final String SERVICE_CORE_STATE_BROADCAST_EXTRA = "CORE_STATE_EXTRA";
14 | public static final String SERVICE_DURATION_BROADCAST_EXTRA = "SERVICE_DURATION_EXTRA";
15 | public static final String SERVICE_UPLOAD_SPEED_BROADCAST_EXTRA = "UPLOAD_SPEED_EXTRA";
16 | public static final String SERVICE_DOWNLOAD_SPEED_BROADCAST_EXTRA = "DOWNLOAD_SPEED_EXTRA";
17 | public static final String SERVICE_DOWNLOAD_TRAFFIC_BROADCAST_EXTRA = "DOWNLOADED_TRAFFIC_EXTRA";
18 | public static final String SERVICE_UPLOAD_TRAFFIC_BROADCAST_EXTRA = "UPLOADED_TRAFFIC_EXTRA";
19 | public static final long BYTE = 1;
20 | public static final long KILO_BYTE = BYTE * 1024;
21 | public static final long MEGA_BYTE = KILO_BYTE * 1024;
22 | public static final long GIGA_BYTE = MEGA_BYTE * 1024;
23 |
24 | public enum SERVICE_MODES {
25 | VPN_MODE,
26 | PROXY_MODE
27 | }
28 |
29 | public enum SERVICE_COMMANDS {
30 | START_SERVICE,
31 | STOP_SERVICE,
32 | MEASURE_DELAY
33 | }
34 |
35 | public enum CONNECTION_STATES {
36 | CONNECTED,
37 | CONNECTING,
38 | DISCONNECTED,
39 | }
40 |
41 | public enum CORE_STATES {
42 | RUNNING,
43 | IDLE,
44 | STOPPED,
45 | }
46 | public static final String DEFAULT_OUT_BOUND_PLACE_IN_FULL_JSON_CONFIG = "CONFIG_PROXY_OUTBOUND_PLACE";
47 | public static final String DEFAULT_FULL_JSON_CONFIG = "{\n" +
48 | " \"dns\": {\n" +
49 | " \"hosts\": {\n" +
50 | " \"domain:googleapis.cn\": \"googleapis.com\"\n" +
51 | " },\n" +
52 | " \"servers\": [\n" +
53 | " \"1.1.1.1\"\n" +
54 | " ]\n" +
55 | " },\n" +
56 | " \"inbounds\": [\n" +
57 | " {\n" +
58 | " \"listen\": \"127.0.0.1\",\n" +
59 | " \"port\": 10808,\n" +
60 | " \"protocol\": \"socks\",\n" +
61 | " \"settings\": {\n" +
62 | " \"auth\": \"noauth\",\n" +
63 | " \"udp\": true,\n" +
64 | " \"userLevel\": 8\n" +
65 | " },\n" +
66 | " \"sniffing\": {\n" +
67 | " \"destOverride\": [],\n" +
68 | " \"enabled\": false\n" +
69 | " },\n" +
70 | " \"tag\": \"socks\"\n" +
71 | " },\n" +
72 | " {\n" +
73 | " \"listen\": \"127.0.0.1\",\n" +
74 | " \"port\": 10809,\n" +
75 | " \"protocol\": \"http\",\n" +
76 | " \"settings\": {\n" +
77 | " \"userLevel\": 8\n" +
78 | " },\n" +
79 | " \"tag\": \"http\"\n" +
80 | " }\n" +
81 | " ],\n" +
82 | " \"log\": {\n" +
83 | " \"loglevel\": \"error\"\n" +
84 | " },\n" +
85 | " \"outbounds\": [\n" +
86 | " "+DEFAULT_OUT_BOUND_PLACE_IN_FULL_JSON_CONFIG+",\n" +
87 | " {\n" +
88 | " \"protocol\": \"freedom\",\n" +
89 | " \"settings\": {},\n" +
90 | " \"tag\": \"direct\"\n" +
91 | " },\n" +
92 | " {\n" +
93 | " \"protocol\": \"blackhole\",\n" +
94 | " \"settings\": {\n" +
95 | " \"response\": {\n" +
96 | " \"type\": \"http\"\n" +
97 | " }\n" +
98 | " },\n" +
99 | " \"tag\": \"block\"\n" +
100 | " }\n" +
101 | " ],\n" +
102 | " \"remarks\": \"test\",\n" +
103 | " \"routing\": {\n" +
104 | " \"domainStrategy\": \"IPIfNonMatch\",\n" +
105 | " \"rules\": [\n" +
106 | " {\n" +
107 | " \"ip\": [\n" +
108 | " \"1.1.1.1\"\n" +
109 | " ],\n" +
110 | " \"outboundTag\": \"proxy\",\n" +
111 | " \"port\": \"53\",\n" +
112 | " \"type\": \"field\"\n" +
113 | " }\n" +
114 | " ]\n" +
115 | " }\n" +
116 | "}";
117 | }
118 |
--------------------------------------------------------------------------------