diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5edb4ee
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.iml
+.gradle
+/local.properties
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
new file mode 100644
index 0000000..bc6a634
Binary files /dev/null and b/.idea/caches/build_file_checksums.ser differ
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..30aa626
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..99202cc
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100755
index 0000000..23cb790
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..2d9b418
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 27
+ defaultConfig {
+ applicationId "com.zzy.vpnservicedemo"
+ minSdkVersion 16
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.0'
+ implementation 'com.android.support:design:27.1.1'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.1'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/app/src/androidTest/java/com/zzy/vpnservicedemo/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/zzy/vpnservicedemo/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3b20faa
--- /dev/null
+++ b/app/src/androidTest/java/com/zzy/vpnservicedemo/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.zzy.vpnservicedemo;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.zzy.vpnservicedemo", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c5db1a0
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/zzy/vpnservicedemo/ByteBufferPool.java b/app/src/main/java/com/zzy/vpnservicedemo/ByteBufferPool.java
new file mode 100755
index 0000000..1b48e65
--- /dev/null
+++ b/app/src/main/java/com/zzy/vpnservicedemo/ByteBufferPool.java
@@ -0,0 +1,60 @@
+/*
+** Copyright 2015, Mohamed Naufal
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.zzy.vpnservicedemo;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class ByteBufferPool
+{
+ private static final int BUFFER_SIZE = 16384;
+ private static ConcurrentLinkedQueue pool = new ConcurrentLinkedQueue();
+
+ public static ByteBuffer acquire()
+ {
+ synchronized (pool)
+ {
+ ByteBuffer buffer = pool.poll();
+ if (buffer == null)
+ {
+ buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
+ }
+ buffer.clear();
+ return buffer;
+ }
+
+ }
+
+ public static void release(ByteBuffer buffer)
+ {
+ synchronized (pool)
+ {
+ buffer.clear();
+ // pool.offer(buffer);
+ }
+
+ }
+
+ public static void clear()
+ {
+ synchronized (pool)
+ {
+ pool.clear();
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/zzy/vpnservicedemo/DemoService.java b/app/src/main/java/com/zzy/vpnservicedemo/DemoService.java
new file mode 100755
index 0000000..c909d0d
--- /dev/null
+++ b/app/src/main/java/com/zzy/vpnservicedemo/DemoService.java
@@ -0,0 +1,231 @@
+package com.zzy.vpnservicedemo;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class DemoService extends VpnService
+{
+
+ private static final String TAG = "DemoService";
+ public static final String VPN_ADDRESS = "168.168.168.168";
+ private static final String VPN_ROUTE = "0.0.0.0";
+ private static final String VPN_DNS = "192.168.1.1";
+
+ public static final String BROADCAST_VPN_STATE = "com.vpn.status";
+ public static final String BROADCAST_STOP_VPN = "com.vpn.stop";
+
+ private ParcelFileDescriptor vpnInterface = null;
+ private ExecutorService executorService;
+ private VPNRunnable vpnRunnable;
+
+
+ @Override
+ public void onCreate()
+ {
+ super.onCreate();
+
+ registerReceiver(stopReceiver, new IntentFilter(BROADCAST_STOP_VPN));
+ if(setupVPN()) {
+
+ sendBroadcast(new Intent(BROADCAST_VPN_STATE).putExtra("running", true));
+ vpnRunnable = new VPNRunnable(vpnInterface);
+ executorService = Executors.newFixedThreadPool(1);
+ executorService.submit(vpnRunnable);
+ }
+ }
+
+ private boolean setupVPN()
+ {
+ try
+ {
+ if (vpnInterface == null)
+ {
+ Builder builder = new Builder();
+ builder.addAddress(VPN_ADDRESS, 24);
+ builder.addRoute(VPN_ROUTE, 0);
+
+ Intent configure = new Intent(this, MainActivity.class);
+ PendingIntent pi = PendingIntent.getActivity(this, 0, configure, PendingIntent.FLAG_UPDATE_CURRENT);
+ builder.setConfigureIntent(pi);
+
+ vpnInterface = builder.setSession(getString(R.string.app_name)).establish();
+
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ private void stopVpn()
+ {
+
+ if(vpnRunnable !=null) {
+ vpnRunnable.stop();
+ }
+ if(vpnInterface !=null) {
+ try {
+ vpnInterface.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ vpnInterface = null;
+ vpnRunnable = null;
+ executorService = null;
+
+ sendBroadcast(new Intent(BROADCAST_VPN_STATE).putExtra("running", false));
+ }
+
+
+ private BroadcastReceiver stopReceiver = new BroadcastReceiver()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ if (intent == null || intent.getAction() == null)
+ {
+ return;
+ }
+
+ if (BROADCAST_STOP_VPN.equals(intent.getAction()))
+ {
+ onRevoke();
+ stopVpn();
+
+ }
+ }
+ };
+
+ @Override
+ public void onDestroy()
+ {
+ super.onDestroy();
+ stopVpn();
+ unregisterReceiver(stopReceiver);
+ }
+
+ private static class VPNRunnable implements Runnable
+ {
+ private static final String TAG = VPNRunnable.class.getSimpleName();
+ ParcelFileDescriptor vpnInterface;
+ private boolean isStop;
+
+ public VPNRunnable(ParcelFileDescriptor vpnInterface)
+ {
+ isStop = false;
+ this.vpnInterface = vpnInterface;
+ }
+
+ public void stop()
+ {
+ isStop = true;
+ }
+
+ @Override
+ public void run()
+ {
+ FileChannel vpnInput = new FileInputStream(vpnInterface.getFileDescriptor()).getChannel();
+ FileChannel vpnOutput = new FileOutputStream(vpnInterface.getFileDescriptor()).getChannel();
+
+ ByteBuffer bufferToNetwork = null;
+ while (true)
+ {
+ if(isStop)
+ {
+ vpnInterface = null;
+ break;
+ }
+
+ if (bufferToNetwork != null)
+ {
+ bufferToNetwork.clear();
+ }
+ else
+ {
+ bufferToNetwork = ByteBufferPool.acquire();
+ }
+
+ int readBytes = 0;
+ try {
+ readBytes = vpnInput.read(bufferToNetwork);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+
+ if (readBytes > 0)
+ {
+ bufferToNetwork.flip();
+ Packet packet = null;
+ try
+ {
+ packet = new Packet(bufferToNetwork, false);
+ }
+ catch (UnknownHostException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ String sIp = null;
+ if(packet.ip4Header.destinationAddress !=null)
+ {
+ sIp = packet.ip4Header.destinationAddress.getHostAddress();
+ }
+
+
+ if (packet.isUDP())
+ {
+ Log.i(TAG,"udp address:" + packet.ip4Header.sourceAddress.getHostAddress() + " udp port:"
+ + packet.udpHeader.sourcePort + " des:" + sIp + " des port:" + packet.udpHeader.destinationPort);
+
+ }
+ else if (packet.isTCP())
+ {
+
+ Log.i(TAG,"tcp address:" + packet.ip4Header.sourceAddress.getHostAddress() + "tcp port:"
+ + packet.tcpHeader.sourcePort + " des:" + sIp + " des port:" + packet.tcpHeader.destinationPort);
+
+ }
+ else if (packet.isPing())
+ {
+ Log.w(TAG, packet.ip4Header.toString());
+ }
+ else
+ {
+ Log.w(TAG, "Unknown packet type");
+ Log.w(TAG, packet.ip4Header.toString());
+ }
+ }
+
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+ }
+}
diff --git a/app/src/main/java/com/zzy/vpnservicedemo/MainActivity.java b/app/src/main/java/com/zzy/vpnservicedemo/MainActivity.java
new file mode 100644
index 0000000..2d09197
--- /dev/null
+++ b/app/src/main/java/com/zzy/vpnservicedemo/MainActivity.java
@@ -0,0 +1,148 @@
+package com.zzy.vpnservicedemo;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.VpnService;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Button;
+
+public class MainActivity extends AppCompatActivity {
+
+ private static final int VPN_REQUEST_CODE = 0x0F;
+ private Button btnStart;
+ private boolean isStart;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
+ .setAction("Action", null).show();
+ }
+ });
+
+ registerReceiver(vpnStateReceiver, new IntentFilter(DemoService.BROADCAST_VPN_STATE));
+ btnStart = findViewById(R.id.btnStart);
+ btnStart.setText("start");
+ btnStart.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if(!isStart) {
+ startVPN();
+ }else{
+ sendBroadcast(new Intent(DemoService.BROADCAST_STOP_VPN));
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private Handler handler = new Handler(new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ return false;
+ }
+ });
+
+ private Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+
+ stopService(new Intent(MainActivity.this, DemoService.class));
+ }
+ };
+
+
+ private BroadcastReceiver vpnStateReceiver = new BroadcastReceiver()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ if (DemoService.BROADCAST_VPN_STATE.equals(intent.getAction()))
+ {
+ if (intent.getBooleanExtra("running", false))
+ {
+ isStart = true;
+ btnStart.setText("stop");
+ }
+ else
+ {
+ isStart =false;
+ btnStart.setText("start");
+ handler.postDelayed(runnable,200);
+ }
+ }
+ }
+ };
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == VPN_REQUEST_CODE && resultCode == RESULT_OK)
+ {
+ startService(new Intent(this, DemoService.class));
+ }
+ }
+
+ private void startVPN()
+ {
+ Intent vpnIntent = VpnService.prepare(this);
+ if (vpnIntent != null)
+ {
+ startActivityForResult(vpnIntent, VPN_REQUEST_CODE);
+ }
+ else
+ {
+ onActivityResult(VPN_REQUEST_CODE, RESULT_OK, null);
+ }
+ }
+
+ @Override
+ protected void onDestroy()
+ {
+ super.onDestroy();
+ handler.removeCallbacks(runnable);
+ unregisterReceiver(vpnStateReceiver);
+ }
+
+}
diff --git a/app/src/main/java/com/zzy/vpnservicedemo/Packet.java b/app/src/main/java/com/zzy/vpnservicedemo/Packet.java
new file mode 100755
index 0000000..21ac732
--- /dev/null
+++ b/app/src/main/java/com/zzy/vpnservicedemo/Packet.java
@@ -0,0 +1,579 @@
+/*
+** Copyright 2015, Mohamed Naufal
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.zzy.vpnservicedemo;
+
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+
+/**
+ * Representation of an IP Packet
+ */
+// TODO: Reduce public mutability
+public class Packet
+{
+ public static final int IP4_HEADER_SIZE = 20;
+ public static final int TCP_HEADER_SIZE = 20;
+ public static final int UDP_HEADER_SIZE = 8;
+
+ public IP4Header ip4Header;
+ public TCPHeader tcpHeader;
+ public UDPHeader udpHeader;
+ public ByteBuffer backingBuffer;
+
+ private boolean isTCP;
+ private boolean isUDP;
+ private boolean isPing;
+ private boolean bForbin=false;
+ private static final String TAG = "Packet";
+
+ public Packet(ByteBuffer buffer, boolean bForbin) throws UnknownHostException {
+ this.ip4Header = new IP4Header(buffer);
+ if (this.ip4Header.protocol == IP4Header.TransportProtocol.TCP) {
+ this.tcpHeader = new TCPHeader(buffer);
+ this.isTCP = true;
+ } else if (ip4Header.protocol == IP4Header.TransportProtocol.UDP) {
+ this.udpHeader = new UDPHeader(buffer);
+ this.isUDP = true;
+ }
+ else if (ip4Header.protocol == IP4Header.TransportProtocol.PING) {
+ this.isPing = true;
+ }
+ this.backingBuffer = buffer;
+ this.bForbin = bForbin;
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder("Packet{");
+ sb.append("ip4Header=").append(ip4Header);
+ if (isTCP) sb.append(", tcpHeader=").append(tcpHeader);
+ else if (isUDP) sb.append(", udpHeader=").append(udpHeader);
+ sb.append(", payloadSize=").append(backingBuffer.limit() - backingBuffer.position());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public void setForbin()
+ {
+ bForbin = true;
+ }
+
+ public boolean isForbin()
+ {
+ return bForbin;
+ }
+
+ public boolean isTCP()
+ {
+ return isTCP;
+ }
+
+ public boolean isUDP()
+ {
+ return isUDP;
+ }
+
+ public boolean isPing()
+ {
+ return isPing;
+ }
+
+ public void swapSourceAndDestination()
+ {
+ InetAddress newSourceAddress = ip4Header.destinationAddress;
+ ip4Header.destinationAddress = ip4Header.sourceAddress;
+ ip4Header.sourceAddress = newSourceAddress;
+
+ if (isUDP)
+ {
+ int newSourcePort = udpHeader.destinationPort;
+ udpHeader.destinationPort = udpHeader.sourcePort;
+ udpHeader.sourcePort = newSourcePort;
+ }
+ else if (isTCP)
+ {
+ int newSourcePort = tcpHeader.destinationPort;
+ tcpHeader.destinationPort = tcpHeader.sourcePort;
+ tcpHeader.sourcePort = newSourcePort;
+ }
+ }
+
+ public void updateTCPBuffer(ByteBuffer buffer, byte flags, long sequenceNum, long ackNum, int payloadSize)
+ {
+ //buffer.position(0);
+ buffer.clear();
+ try
+ {
+ fillHeader(buffer);
+ backingBuffer = buffer;
+
+ tcpHeader.flags = flags;
+ backingBuffer.put(IP4_HEADER_SIZE + 13, flags);
+
+ tcpHeader.sequenceNumber = sequenceNum;
+ backingBuffer.putInt(IP4_HEADER_SIZE + 4, (int) sequenceNum);
+
+ tcpHeader.acknowledgementNumber = ackNum;
+ backingBuffer.putInt(IP4_HEADER_SIZE + 8, (int) ackNum);
+
+ // Reset header size, since we don't need options
+ byte dataOffset = (byte) (TCP_HEADER_SIZE << 2);
+ tcpHeader.dataOffsetAndReserved = dataOffset;
+ backingBuffer.put(IP4_HEADER_SIZE + 12, dataOffset);
+
+ updateTCPChecksum(payloadSize);
+
+ int ip4TotalLength = IP4_HEADER_SIZE + TCP_HEADER_SIZE + payloadSize;
+ backingBuffer.putShort(2, (short) ip4TotalLength);
+ ip4Header.totalLength = ip4TotalLength;
+
+ updateIP4Checksum();
+
+ boolean isSyn = tcpHeader.isSYN();
+ boolean isRst = tcpHeader.isRST();
+ boolean isFIN = tcpHeader.isFIN();
+ boolean isAck = tcpHeader.isACK();
+ boolean isPsh = tcpHeader.isPSH();
+
+ Log.i(TAG,"fillHeader: sourceport" + tcpHeader.sourcePort
+ +" destinationPort:"+ tcpHeader.destinationPort
+ +" sourceAddress:"+ip4Header.sourceAddress.getHostAddress()
+ +" destinationAddress:"+ ip4Header.destinationAddress.getHostAddress()
+ +" sequenceNumber:"+tcpHeader.sequenceNumber
+ +" acknowledgementNumber:"+ tcpHeader.acknowledgementNumber
+ +" Syn:"+isSyn
+ +" Rst:"+isRst
+ +" FIN:"+isFIN
+ +" Ack:"+isAck
+ +" Psh:"+isPsh);
+ }
+ catch (Exception e)
+ {
+ int iLimit = buffer.limit();
+ int iCap = buffer.capacity();
+ int iposition = buffer.position();
+ int iRemain = buffer.remaining();
+ Log.i(TAG,"updateTCPBuffer limit:"+iLimit+" cap:"+iCap+" position:"+iposition+" remain:"+iRemain);
+ e.printStackTrace();
+ }
+ }
+
+ public void updateUDPBuffer(ByteBuffer buffer, int payloadSize)
+ {
+ buffer.position(0);
+ fillHeader(buffer);
+ backingBuffer = buffer;
+
+ int udpTotalLength = UDP_HEADER_SIZE + payloadSize;
+ backingBuffer.putShort(IP4_HEADER_SIZE + 4, (short) udpTotalLength);
+ udpHeader.length = udpTotalLength;
+
+ // Disable UDP checksum validation
+ backingBuffer.putShort(IP4_HEADER_SIZE + 6, (short) 0);
+ udpHeader.checksum = 0;
+
+ int ip4TotalLength = IP4_HEADER_SIZE + udpTotalLength;
+ backingBuffer.putShort(2, (short) ip4TotalLength);
+ ip4Header.totalLength = ip4TotalLength;
+
+ updateIP4Checksum();
+ }
+
+ private void updateIP4Checksum()
+ {
+ ByteBuffer buffer = backingBuffer.duplicate();
+ buffer.position(0);
+
+ // Clear previous checksum
+ buffer.putShort(10, (short) 0);
+
+ int ipLength = ip4Header.headerLength;
+ int sum = 0;
+ while (ipLength > 0)
+ {
+ sum += BitUtils.getUnsignedShort(buffer.getShort());
+ ipLength -= 2;
+ }
+ while (sum >> 16 > 0)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ sum = ~sum;
+ ip4Header.headerChecksum = sum;
+ backingBuffer.putShort(10, (short) sum);
+ }
+
+ private void updateTCPChecksum(int payloadSize)
+ {
+ int sum = 0;
+ int tcpLength = TCP_HEADER_SIZE + payloadSize;
+
+ // Calculate pseudo-header checksum
+ ByteBuffer buffer = ByteBuffer.wrap(ip4Header.sourceAddress.getAddress());
+ sum = BitUtils.getUnsignedShort(buffer.getShort()) + BitUtils.getUnsignedShort(buffer.getShort());
+
+ buffer = ByteBuffer.wrap(ip4Header.destinationAddress.getAddress());
+ sum += BitUtils.getUnsignedShort(buffer.getShort()) + BitUtils.getUnsignedShort(buffer.getShort());
+
+ sum += IP4Header.TransportProtocol.TCP.getNumber() + tcpLength;
+
+ buffer = backingBuffer.duplicate();
+ // Clear previous checksum
+ buffer.putShort(IP4_HEADER_SIZE + 16, (short) 0);
+
+ // Calculate TCP segment checksum
+ buffer.position(IP4_HEADER_SIZE);
+ while (tcpLength > 1)
+ {
+ sum += BitUtils.getUnsignedShort(buffer.getShort());
+ tcpLength -= 2;
+ }
+ if (tcpLength > 0)
+ sum += BitUtils.getUnsignedByte(buffer.get()) << 8;
+
+ while (sum >> 16 > 0)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ sum = ~sum;
+ tcpHeader.checksum = sum;
+ backingBuffer.putShort(IP4_HEADER_SIZE + 16, (short) sum);
+ }
+
+ private void fillHeader(ByteBuffer buffer)
+ {
+ ip4Header.fillHeader(buffer);
+ if (isUDP)
+ udpHeader.fillHeader(buffer);
+ else if (isTCP)
+ tcpHeader.fillHeader(buffer);
+ }
+
+ public static class IP4Header
+ {
+ public byte version;
+ public byte IHL;
+ public int headerLength;
+ public short typeOfService;
+ public int totalLength;
+
+ public int identificationAndFlagsAndFragmentOffset;
+
+ public short TTL;
+ private short protocolNum;
+ public TransportProtocol protocol;
+ public int headerChecksum;
+
+ public InetAddress sourceAddress;
+ public InetAddress destinationAddress;
+
+ public int iSourceAddress;
+ public int iDestinationAddress;
+
+ public int optionsAndPadding;
+
+ public enum TransportProtocol
+ {
+ TCP(6),
+ UDP(17),
+ PING(1),
+ Other(0xFF);
+
+ private int protocolNumber;
+
+ TransportProtocol(int protocolNumber)
+ {
+ this.protocolNumber = protocolNumber;
+ }
+
+ private static TransportProtocol numberToEnum(int protocolNumber)
+ {
+ if (protocolNumber == 6)
+ {
+ return TCP;
+ }
+ else if (protocolNumber == 17)
+ {
+ return UDP;
+ }
+ else if (protocolNumber == 1)
+ {
+ return PING;
+ }
+ else
+ {
+ Log.i(TAG,"numberToEnum protocolNumber:"+protocolNumber);
+ return Other;
+ }
+ }
+
+ public int getNumber()
+ {
+ return this.protocolNumber;
+ }
+ }
+
+ private IP4Header(ByteBuffer buffer) throws UnknownHostException
+ {
+ byte versionAndIHL = buffer.get();
+ this.version = (byte) (versionAndIHL >> 4);
+ this.IHL = (byte) (versionAndIHL & 0x0F);
+ this.headerLength = this.IHL << 2;
+
+ this.typeOfService = BitUtils.getUnsignedByte(buffer.get());
+ this.totalLength = BitUtils.getUnsignedShort(buffer.getShort());
+
+ this.identificationAndFlagsAndFragmentOffset = buffer.getInt();
+
+ this.TTL = BitUtils.getUnsignedByte(buffer.get());
+ this.protocolNum = BitUtils.getUnsignedByte(buffer.get());
+ this.protocol = TransportProtocol.numberToEnum(protocolNum);
+ this.headerChecksum = BitUtils.getUnsignedShort(buffer.getShort());
+
+ byte[] addressBytes = new byte[4];
+ buffer.get(addressBytes, 0, 4);
+ this.sourceAddress = InetAddress.getByAddress(addressBytes);
+ this.iSourceAddress = bytesToInt(addressBytes);
+
+ buffer.get(addressBytes, 0, 4);
+ this.destinationAddress = InetAddress.getByAddress(addressBytes);
+ this.iDestinationAddress = bytesToInt(addressBytes);
+
+ //this.optionsAndPadding = buffer.getInt();
+ }
+
+ public static int bytesToInt(byte[] bytes)
+ {
+ int addr = bytes[3] & 0xFF;
+ addr |= ((bytes[2] << 8) & 0xFF00);
+ addr |= ((bytes[1] << 16) & 0xFF0000);
+ addr |= ((bytes[0] << 24) & 0xFF000000);
+ return addr;
+ }
+
+ public void fillHeader(ByteBuffer buffer)
+ {
+ buffer.put((byte) (this.version << 4 | this.IHL));
+ buffer.put((byte) this.typeOfService);
+ buffer.putShort((short) this.totalLength);
+
+ buffer.putInt(this.identificationAndFlagsAndFragmentOffset);
+
+ buffer.put((byte) this.TTL);
+ buffer.put((byte) this.protocol.getNumber());
+ buffer.putShort((short) this.headerChecksum);
+
+ buffer.put(this.sourceAddress.getAddress());
+ buffer.put(this.destinationAddress.getAddress());
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder("IP4Header{");
+ sb.append("version=").append(version);
+ sb.append(", IHL=").append(IHL);
+ sb.append(", typeOfService=").append(typeOfService);
+ sb.append(", totalLength=").append(totalLength);
+ sb.append(", identificationAndFlagsAndFragmentOffset=").append(identificationAndFlagsAndFragmentOffset);
+ sb.append(", TTL=").append(TTL);
+ sb.append(", protocol=").append(protocolNum).append(":").append(protocol);
+ sb.append(", headerChecksum=").append(headerChecksum);
+ sb.append(", sourceAddress=").append(sourceAddress.getHostAddress());
+ sb.append(", destinationAddress=").append(destinationAddress.getHostAddress());
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static class TCPHeader
+ {
+ public static final int FIN = 0x01;
+ public static final int SYN = 0x02;
+ public static final int RST = 0x04;
+ public static final int PSH = 0x08;
+ public static final int ACK = 0x10;
+ public static final int URG = 0x20;
+
+ public int sourcePort;
+ public int destinationPort;
+
+ public long sequenceNumber;
+ public long acknowledgementNumber;
+
+ public byte dataOffsetAndReserved;
+ public int headerLength;
+ public byte flags;
+ public int window;
+
+ public int checksum;
+ public int urgentPointer;
+
+ public byte[] optionsAndPadding;
+
+ private TCPHeader(ByteBuffer buffer)
+ {
+ this.sourcePort = BitUtils.getUnsignedShort(buffer.getShort());
+ this.destinationPort = BitUtils.getUnsignedShort(buffer.getShort());
+
+ this.sequenceNumber = BitUtils.getUnsignedInt(buffer.getInt());
+ this.acknowledgementNumber = BitUtils.getUnsignedInt(buffer.getInt());
+
+ this.dataOffsetAndReserved = buffer.get();
+ this.headerLength = (this.dataOffsetAndReserved & 0xF0) >> 2;
+ this.flags = buffer.get();
+ this.window = BitUtils.getUnsignedShort(buffer.getShort());
+
+ this.checksum = BitUtils.getUnsignedShort(buffer.getShort());
+ this.urgentPointer = BitUtils.getUnsignedShort(buffer.getShort());
+
+ int optionsLength = this.headerLength - TCP_HEADER_SIZE;
+ if (optionsLength > 0)
+ {
+ optionsAndPadding = new byte[optionsLength];
+ buffer.get(optionsAndPadding, 0, optionsLength);
+ }
+ }
+
+ public boolean isFIN()
+ {
+ return (flags & FIN) == FIN;
+ }
+
+ public boolean isSYN()
+ {
+ return (flags & SYN) == SYN;
+ }
+
+ public boolean isRST()
+ {
+ return (flags & RST) == RST;
+ }
+
+ public boolean isPSH()
+ {
+ return (flags & PSH) == PSH;
+ }
+
+ public boolean isACK()
+ {
+ return (flags & ACK) == ACK;
+ }
+
+ public boolean isURG()
+ {
+ return (flags & URG) == URG;
+ }
+
+ private void fillHeader(ByteBuffer buffer)
+ {
+ buffer.putShort((short) sourcePort);
+ buffer.putShort((short) destinationPort);
+
+ buffer.putInt((int) sequenceNumber);
+ buffer.putInt((int) acknowledgementNumber);
+
+ buffer.put(dataOffsetAndReserved);
+ buffer.put(flags);
+ buffer.putShort((short) window);
+
+ buffer.putShort((short) checksum);
+ buffer.putShort((short) urgentPointer);
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder("TCPHeader{");
+ sb.append("sourcePort=").append(sourcePort);
+ sb.append(", destinationPort=").append(destinationPort);
+ sb.append(", sequenceNumber=").append(sequenceNumber);
+ sb.append(", acknowledgementNumber=").append(acknowledgementNumber);
+ sb.append(", headerLength=").append(headerLength);
+ sb.append(", window=").append(window);
+ sb.append(", checksum=").append(checksum);
+ sb.append(", flags=");
+ if (isFIN()) sb.append(" FIN");
+ if (isSYN()) sb.append(" SYN");
+ if (isRST()) sb.append(" RST");
+ if (isPSH()) sb.append(" PSH");
+ if (isACK()) sb.append(" ACK");
+ if (isURG()) sb.append(" URG");
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static class UDPHeader
+ {
+ public int sourcePort;
+ public int destinationPort;
+
+ public int length;
+ public int checksum;
+
+ private UDPHeader(ByteBuffer buffer)
+ {
+ this.sourcePort = BitUtils.getUnsignedShort(buffer.getShort());
+ this.destinationPort = BitUtils.getUnsignedShort(buffer.getShort());
+
+ this.length = BitUtils.getUnsignedShort(buffer.getShort());
+ this.checksum = BitUtils.getUnsignedShort(buffer.getShort());
+ }
+
+ private void fillHeader(ByteBuffer buffer)
+ {
+ buffer.putShort((short) this.sourcePort);
+ buffer.putShort((short) this.destinationPort);
+
+ buffer.putShort((short) this.length);
+ buffer.putShort((short) this.checksum);
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder("UDPHeader{");
+ sb.append("sourcePort=").append(sourcePort);
+ sb.append(", destinationPort=").append(destinationPort);
+ sb.append(", length=").append(length);
+ sb.append(", checksum=").append(checksum);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ private static class BitUtils
+ {
+ private static short getUnsignedByte(byte value)
+ {
+ return (short)(value & 0xFF);
+ }
+
+ private static int getUnsignedShort(short value)
+ {
+ return value & 0xFFFF;
+ }
+
+ private static long getUnsignedInt(int value)
+ {
+ return value & 0xFFFFFFFFL;
+ }
+ }
+}
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..c7bd21d
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..d5fccc5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..eed4d89
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
new file mode 100644
index 0000000..abbbeef
--- /dev/null
+++ b/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..69d7f6a
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,10 @@
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a2f5908
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1b52399
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..ff10afd
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..115a4c7
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..dcd3cd8
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..459ca60
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..8ca12fe
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..8e19b41
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b824ebd
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4c19a13
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..59a0b0c
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,3 @@
+
+ 16dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..3034af3
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ VpnServiceDemo
+ Settings
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..545b9c6
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/com/zzy/vpnservicedemo/ExampleUnitTest.java b/app/src/test/java/com/zzy/vpnservicedemo/ExampleUnitTest.java
new file mode 100644
index 0000000..d9abedf
--- /dev/null
+++ b/app/src/test/java/com/zzy/vpnservicedemo/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.zzy.vpnservicedemo;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..eccff49
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.1.1'
+
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..743d692
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,13 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..7a3265e
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4ace501
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Apr 20 15:40:40 CST 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app'