Compare commits

...
121 Commits
Author SHA1 Message Date
Max Lv 6ec8117e1c Update shadowsocks-libev 2016-09-20 18:01:10 +08:00
Max Lv 3c68a26794 Update DNS forwarding 2016-09-20 08:40:13 +08:00
Max Lv fda89933d0 Disable DNS forwarding in GFWList/ChinaList mode 2016-09-19 17:18:39 +08:00
Max Lv d8f721db3b Refine DNS resolve of GFWList and ChinaList, #887 2016-09-19 09:58:36 +08:00
Max Lv 28339ce7c1 Update DNS 2016-09-19 09:10:47 +08:00
Max Lv edda3179dd Add China List, close #886 2016-09-18 18:56:04 +08:00
Mygod 1d79990656 Remove workaround for libsuperuser#55 2016-09-15 12:22:21 +08:00
Mygod 2a47362195 Fix profile name not updated in ShadowsocksTileService 2016-09-15 00:11:37 +08:00
Mygod 02d5951f8b Auto reset when downgrading
While it's theoretically impossible to downgrade without reinstalling the app, there are ways to bypass this mechanism. And don't forget users can restore their app data as well.
2016-09-14 22:37:26 +08:00
Mygod b126c62365 Fix a crash in AppManager 2016-09-14 18:16:44 +08:00
Mygod bbaf1779ea Add missing translation for Russian 2016-09-14 17:18:10 +08:00
Max Lv 65ddd6f5d8 Bump version 2016-09-14 14:57:02 +08:00
Max Lv 772bc18eb8 Bump version 2016-09-12 10:28:25 +08:00
Max Lv d503818169 Add missing translation 2016-09-12 10:27:55 +08:00
Max Lv b209e73386 Bump version 2016-09-12 10:11:16 +08:00
Max Lv 6c381b0ecc Fix a crash reported by Goolge Play 2016-09-12 09:49:17 +08:00
Max Lv e3f0fa6756 Fix #871 2016-09-09 22:13:51 +08:00
Max Lv 35cb273592 SuppressLint 2016-09-09 16:47:47 +08:00
Max Lv b76f410c34 Refine header parser 2016-09-09 10:30:22 +08:00
Max Lv d3678531b7 Bump version 2016-09-08 19:23:50 +08:00
Max Lv 122ca599de Add GFWList support 2016-09-08 18:36:39 +08:00
Max Lv 96fcec788c Add issue template 2016-09-05 16:24:51 +08:00
Max Lv 1c67b39a3e Bump version 2016-09-04 10:31:33 +08:00
Max Lv 30fea4a705 Merge branch 'master' of github.com:shadowsocks/shadowsocks-android 2016-09-04 10:19:15 +08:00
Max Lv 7c0763c78a Fix #848 2016-09-01 14:00:23 +08:00
Mygod b8c593b110 Fix crash in GuardedProcess 2016-08-29 13:57:25 +08:00
Mygod 782af959a0 Fix #844 2016-08-29 12:54:55 +08:00
Max Lv 6df227d231 Update kcptun 2016-08-28 18:11:29 +08:00
Max Lv 5b1432e4e8 Handle stdout/stderr of GuardedProcess properly 2016-08-28 17:42:19 +08:00
Max Lv 7ca4e4ec74 Enable android log for kcptun 2016-08-28 08:00:35 +08:00
Mygod 655ef1eed6 Refine TFO 2016-08-27 23:39:06 +08:00
Mygod 04be60577a Revert "Revert "Fix #839 #838""
This reverts commit 264b177b38.
2016-08-27 23:32:13 +08:00
Mygod 63d2ec7b02 Revert "Fix #838 #839"
This reverts commit 32f4d6af47.

Never mind then! I changed my mind.
2016-08-27 23:31:50 +08:00
Mygod 06b6afd557 Update sbt-android 2016-08-27 23:23:58 +08:00
Mygod 32f4d6af47 Fix #838 #839 2016-08-27 23:23:08 +08:00
Mygod 264b177b38 Revert "Fix #839 #838"
This reverts commit 069cc152ca.
2016-08-27 23:15:25 +08:00
Syrone Wong 67a1c5460a Drop redundant rule (#840)
[info] Android NDK: WARNING:jni/Android.mk:mbedtls: LOCAL_LDLIBS is always ignored for static libraries
2016-08-27 20:42:16 +08:00
Max Lv a20b7821f2 Bump version 2016-08-27 17:11:05 +08:00
Max Lv 069cc152ca Fix #839 #838 2016-08-27 17:10:12 +08:00
Max Lv 7b57be669c Bump version 2016-08-27 07:56:10 +08:00
Max Lv 2f051e9eed Update kcptun 2016-08-23 13:52:48 +08:00
Mygod 8d1d018fe1 Remove boot script to disable fast open
Fix #828.
2016-08-23 13:48:05 +08:00
Mygod 3c5bc27750 Refine mount command
Fix #826.
2016-08-22 21:10:41 +08:00
Mygod 6c047eca8e Add error message if failed 2016-08-22 21:10:33 +08:00
Max Lv a31e753841 Update golang 2016-08-21 11:14:09 +08:00
Max Lv c94c509931 KCP with session ID (#823)
* Add sid support

* Update kcptun
2016-08-20 14:09:22 +08:00
Mygod 41417a7546 Refine regex in KcpCliPreference 2016-08-19 19:52:34 +08:00
Mygod 25cfb1623a Remove all the extravagant styles
These styles are either introduced by @AnyOfYou, or exist because of a bug in Android support library. Anyway they can be removed now.
2016-08-19 19:49:43 +08:00
Max Lv c9ce515cca Bump version 2016-08-19 17:57:53 +08:00
Mygod 7802a50603 Fix Travis dependency problem once and for all 2016-08-19 15:53:59 +08:00
Mygod 680a6594f6 Drop compatibility RTL support for Jelly Bean 2016-08-19 15:39:03 +08:00
Mygod b7367835b0 Update support lib 2016-08-19 15:32:02 +08:00
Mygod 25bd5347eb Refine #816 and error handling in startRunner 2016-08-19 15:23:43 +08:00
Max Lv d2a527ed0e Fix #816 2016-08-18 17:49:48 +08:00
Mygod 07ab938719 Prevent createProfile returns null 2016-08-17 12:52:21 +08:00
Mygod b6bfd5bfa0 Fix navigations in ProfileManagerActivity 2016-08-17 12:37:32 +08:00
Mygod f9194df55d Fix accessibility issue for stat and fab 2016-08-17 12:32:27 +08:00
Mygod 958d387b8c Fix su detection 2016-08-17 12:29:17 +08:00
Mygod eee89774df Refine kcpcli help again 2016-08-17 11:50:40 +08:00
Mygod 8454975a13 Fix NAT notifications on Android 4.4 2016-08-17 11:44:22 +08:00
Max Lv 69cb86a7c5 Format the help output of kcptun 2016-08-17 09:09:30 +08:00
Max Lv 45ea903323 Enable fast-open only with ss-local 2016-08-17 08:30:22 +08:00
Mygod f7303e1564 Revert "Promote default settings to kcptun"
This reverts commit 99ff47bd5f.

See discussion at: https://github.com/shadowsocks/kcptun/pull/1
2016-08-16 22:36:18 +08:00
Mygod 99ff47bd5f Promote default settings to kcptun 2016-08-16 22:16:25 +08:00
Mygod 820d71fb07 Add help for kcpcli (#811)
* Add help for kcpcli

* Filter useless parameters
2016-08-16 22:07:01 +08:00
Mygod a9a6e1c1a6 Track caught exceptions (#810) 2016-08-16 21:26:10 +08:00
Mygod 364d873eba Update to AutoCloseable in Java 1.7 2016-08-16 20:51:47 +08:00
Mygod a722257aba Set tcp_fastopen with echo instead of sysctl 2016-08-16 14:48:30 +08:00
Mygod 80d93a84bd Add support for TCP Fast Open (#809)
* Add support for TCP Fast Open

* Remove useless echo done line

* Check return values before proceeding in TcpFastOpen.enabled
2016-08-16 14:37:21 +08:00
Mygod 9b07239ced Refine default kcpcli 2016-08-16 14:02:16 +08:00
Mygod 5c7174fc5e Add default kcpcli 2016-08-16 11:49:14 +08:00
Mygod 536fe92c83 Refine #806 2016-08-16 11:23:21 +08:00
Mygod 29e6deabd0 Update sbt-android 2016-08-16 11:15:14 +08:00
Mygod e6b39444e8 Update to Java 1.7 2016-08-16 10:57:07 +08:00
Mygod 1b480b0163 Order libraryDependencies in alphabetical order
Putting support library after its dependencies could cause build failures on some rare cases.
2016-08-16 10:56:55 +08:00
Mygod c7fc4095ff Remove compatibility code for Jelly Bean 2016-08-16 10:26:52 +08:00
Max Lv 41d585ae22 Drop the support of Android 4.3 and below 2016-08-14 11:40:26 +08:00
Max Lv 63713b1eea Revert toolchain to GCC 4.9 2016-08-14 10:35:04 +08:00
Max Lv dd6cd3977d Fix #806 #804 2016-08-13 17:33:10 +08:00
Max Lv a671ae98b8 Update kcptun 2016-08-12 15:59:34 +08:00
Max Lv 7531eb5f5f Add official mbedtls submodule 2016-08-12 11:17:03 +08:00
Max Lv dca3be8eff Remove our fork of mbedtls 2016-08-12 11:15:41 +08:00
Max Lv a5d620ab02 Replace OpenSSL with mbedTLS 2016-08-12 10:08:11 +08:00
Mygod 0e7dbde4d9 Refine build.sbt 2016-08-11 20:15:13 +08:00
Mygod 7d0277324c Add aes-ctr ciphers
Addresses: https://github.com/shadowsocks/shadowsocks-org/issues/21
2016-08-11 19:28:12 +08:00
Max Lv 08d99caf1c Update kcptun 2016-08-10 13:27:06 +08:00
Max Lv 0a400ef5f9 Trim the KCP parameters 2016-08-09 12:27:32 +08:00
Max Lv cda3650c43 Revert "Fix SecurityException on Asus Fonepads"
This reverts commit f7d422da2b.
2016-08-09 12:10:57 +08:00
Max Lv b1c19f8f03 Bump version 2016-08-09 11:53:13 +08:00
Mygod f7d422da2b Fix SecurityException on Asus Fonepads
This is a bug of their ROMs but the documentation says:

> Must be required by a VpnService, to ensure that only the system can bind to it.
>
> Protection level: signature
>
> Constant Value: "android.permission.BIND_VPN_SERVICE"

And we have `android:exported="false"` set, so I think it's safe to remove this permission restriction.
2016-08-09 10:45:02 +08:00
Mygod 10725c39cb Check QsTile is null 2016-08-09 10:33:03 +08:00
Mygod 4b5c94022b Update sbt-android 2016-08-09 10:16:01 +08:00
Mygod 6afc3f80be Revert "Update dependency (#799)"
This reverts commit a02d19bba0.
2016-08-09 10:12:56 +08:00
Syrone Wong a02d19bba0 Update dependency (#799)
* Run android-update before native building

* Update sbt-android to 1.6.11
2016-08-09 09:36:48 +08:00
Mygod 984e75ae99 Fix incorrect handling of default values on database upgrade 2016-08-09 04:34:51 +08:00
Mygod 3cc1a1602e Fix go install: no install location for directory again 2016-08-08 19:27:24 +08:00
Mygod 72305c5f60 Fix a bug in SummaryEditTextPreference 2016-08-08 18:47:26 +08:00
Mygod 4303c1cd0c Remove signing in Travis builds (#798) 2016-08-08 13:20:10 +08:00
Mygod 5dad89b71e Update prerequisites in README.md 2016-08-08 12:25:11 +08:00
Mygod 109d4728f4 Remove redundant platform-tools install for Travis 2016-08-08 12:12:37 +08:00
Mygod 53b9c1f4af Switch to Android platform on Travis 2016-08-08 11:58:44 +08:00
Mygod 23fc7e9134 Fix go install: no install location for directory error 2016-08-08 03:20:33 +08:00
Mygod e73fc9f881 Refine travis CI 2016-08-08 02:48:06 +08:00
Mygod 941393cab4 Update dependencies 2016-08-08 02:40:12 +08:00
Mygod a6e87cf666 Merge branch 'master' of github.com:shadowsocks/shadowsocks-android 2016-08-08 00:15:38 +08:00
Max Lv 4817539ca2 Fix UDP forward 2016-08-07 10:43:25 +08:00
Max Lv 03c857536d Fix the build scripts 2016-08-07 08:16:55 +08:00
Eva Kane 14038cd152 Fix remove problem (#793) 2016-08-06 23:10:04 +08:00
Syrone Wong 935314a1c5 Fix kcptun license in about page (#792)
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-06 22:37:41 +08:00
Max Lv 7263bf11a4 Update license 2016-08-06 19:36:10 +08:00
Max Lv e766ae2244 Fix the build sequence 2016-08-06 19:15:25 +08:00
Max Lv f3ed157145 Fix travis path 2016-08-06 19:00:45 +08:00
Max Lv eb0d5ceccc Fix travis 2016-08-06 18:57:09 +08:00
Max Lv 2ffbf4c56f Fix a typo 2016-08-06 18:55:48 +08:00
Max Lv 9ee3bab7e0 Update build scripts 2016-08-06 18:47:07 +08:00
Max Lv 70a7317a1e Update kcptun 2016-08-06 18:41:56 +08:00
Max Lv 43caaa684f Add submodules for kcptun 2016-08-06 18:22:27 +08:00
Max Lv 586c5e2ae4 Bump version 2016-08-06 17:25:10 +08:00
Max Lv 416d93062f Update kcptun 2016-08-06 13:01:40 +08:00
Max Lv 66ceed8607 Restart kcptun when switching network 2016-08-06 10:55:33 +08:00
Mygod 81295e4a51 Remove unnecessary padding for connection test text 2016-07-31 09:57:52 -06:00
81 changed files with 4943 additions and 611 deletions
+18
View File
@@ -0,0 +1,18 @@
Please answer these questions before submitting your issue. Thanks!
### What version of Shadowsocks are you using?
### What Android OS version and device are you using?
### What did you do?
### What did you expect to see?
### What did you see instead?
### What is your config in detail (with all sensitive info masked)?
+1 -2
View File
@@ -14,6 +14,7 @@ gen_*
*.class
*.o
local.sbt
.deps
#Intellij IDEA
*.iml
@@ -27,7 +28,5 @@ out/
src/main/obj
src/main/libs
src/main/assets/armeabi-v7a/*
!src/main/assets/armeabi-v7a/kcptun
src/main/assets/x86/*
!src/main/assets/x86/kcptun
jni/Application.mk
+12 -3
View File
@@ -9,9 +9,6 @@
path = src/main/jni/libancillary
url = https://github.com/shadowsocks/libancillary.git
branch = shadowsocks-android
[submodule "src/main/jni/openssl"]
path = src/main/jni/openssl
url = https://github.com/shadowsocks/openssl-android.git
[submodule "src/main/jni/libevent"]
path = src/main/jni/libevent
url = https://github.com/shadowsocks/libevent.git
@@ -24,3 +21,15 @@
path = src/main/jni/pdnsd
url = https://github.com/shadowsocks/pdnsd.git
branch = shadowsocks-android
[submodule "kcptun/kcptun"]
path = kcptun/kcptun
url = https://github.com/shadowsocks/kcptun
[submodule "kcptun/go"]
path = kcptun/go
url = https://github.com/shadowsocks/go
[submodule "src/main/jni/mbedtls"]
path = src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls
[submodule "src/main/jni/pcre"]
path = src/main/jni/pcre
url = https://android.googlesource.com/platform/external/pcre
+35 -22
View File
@@ -1,19 +1,16 @@
language: scala
language: android
jdk:
- oraclejdk8
sudo: false
addons:
apt:
packages:
- gcc-multilib
- g++-multilib
- libstdc++6:i386
- libgcc1:i386
- zlib1g:i386
- libncurses5:i386
env:
global:
- NDK_VERSION=r12b
- NDK_CCACHE=ccache
- GOROOT_BOOTSTRAP=$GOROOT
- ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION}
- SBTPATH=$HOME/.sbt
- PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
scala:
- 2.11.8
@@ -24,17 +21,33 @@ before_cache:
cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt/boot
- $HOME/.ivy2
- $HOME/.sbt
android:
components:
- tools
- build-tools-24.0.1
- extra-android-m2repository
- extra-google-m2repository
install:
- ./travis-ci/setup.sh
- export NDK_CCACHE=ccache
- export ARCH=`uname -m`
- export ANDROID_NDK_HOME=$HOME/.android/android-ndk-r12b
- export ANDROID_HOME=$HOME/.android/android-sdk-linux
- export PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
- cp local.properties.travis local.properties
- >
if [ ! -d "$ANDROID_NDK_HOME" ]; then
mkdir -p $ANDROID_NDK_HOME;
pushd $HOME/.android;
export ARCH=`uname -m`;
wget -q http://dl.google.com/android/repository/android-ndk-${NDK_VERSION}-linux-${ARCH}.zip;
unzip -q android-ndk-${NDK_VERSION}-linux-${ARCH}.zip;
popd;
fi
- >
if [ ! -f "$SBTPATH/sbt" ]; then
pushd $SBTPATH;
wget -q https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt;
chmod a+x sbt;
popd;
fi
script:
- sbt native-build android:package-release
- $SBTPATH/sbt native-build android:package-debug
+8 -25
View File
@@ -12,13 +12,17 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* JDK 1.8
* SBT 0.13.0+
* Android SDK r25+
* Go 1.4+
* Android SDK
- Build Tools 24+ (as required by [`sbt-android`](https://github.com/scala-android/sbt-android))
- Android Support Repository and Google Repository (see `build.sbt` for version)
* Android NDK r12b+
### BUILD
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
* Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk`
* Set environment variable `GOROOT_BOOTSTRAP` to `/path/to/go`
* Create your key following the instructions at https://developer.android.com/studio/publish/app-signing.html
* Put your key in ~/.keystore
* Create `local.properties` from `local.properties.example` with your own key information
@@ -26,33 +30,11 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
```bash
git submodule update --init
# Build the App
sbt native-build clean android:package-release
```
#### BUILD on Mac OS X (with HomeBrew)
* Install Android SDK and NDK by run `brew install android-ndk android-sdk`
* Add `export ANDROID_HOME=/usr/local/Cellar/android-sdk/$version` to your .bashrc , then reopen the shell to load it.
* Add `export ANDROID_NDK_HOME=/usr/local/Cellar/android-ndk/$version` to your .bashrc , then reopen the shell to load it.
* echo "y" | android update sdk --filter tools,platform-tools,build-tools-23.0.2,android-23,extra-google-m2repository --no-ui -a
* echo "y" | android update sdk --filter extra-android-m2repository --no-ui --no-https -a
* Create your key following the instructions at http://developer.android.com/guide/publishing/app-signing.html#cert
* Put your key in ~/.keystore
* Create `local.properties` from `local.properties.example` with your own key information .
* Invoke the building like this
```bash
git submodule update --init
# Build native binaries
./build.sh
# Build the apk
sbt clean android:package-release
```
## OPEN SOURCE LICENSES
* shadowsocks-libev: [GPLv3](https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE)
@@ -62,10 +44,11 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* pdnsd: [GPLv3](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING)
* libev: [GPLv2](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libev/LICENSE)
* libevent: [BSD](https://github.com/shadowsocks/libevent/blob/master/LICENSE)
* kcptun: [MIT](https://github.com/xtaci/kcptun)
### LICENSE
Copyright (C) 2016 by Max Lv <<max.c.lv@gmail.com>> <br/>
Copyright (C) 2016 by Max Lv <<max.c.lv@gmail.com>>
Copyright (C) 2016 by Mygod Studio <<mygodstudio@gmail.com>>
This program is free software: you can redistribute it and/or modify
+16 -23
View File
@@ -10,9 +10,9 @@ scalaVersion := "2.11.8"
compileOrder in Compile := CompileOrder.JavaThenScala
javacOptions ++= Seq("-source", "1.6", "-target", "1.6")
javacOptions ++= Seq("-source", "1.7", "-target", "1.7")
scalacOptions ++= Seq("-target:jvm-1.6", "-Xexperimental")
scalacOptions ++= Seq("-target:jvm-1.7", "-Xexperimental")
ndkJavah in Android := List()
@@ -28,25 +28,22 @@ dexMaxHeap in Android := "4g"
resolvers += Resolver.jcenterRepo
resolvers += "JRAF" at "http://JRAF.org/static/maven/2"
useSupportVectors
libraryDependencies ++= Seq(
"dnsjava" % "dnsjava" % "2.1.7",
"com.github.kevinsawicki" % "http-request" % "6.0",
"eu.chainfire" % "libsuperuser" % "1.0.0.201607041850",
"net.glxn.qrgen" % "android" % "2.0",
"com.google.android.gms" % "play-services-ads" % "9.2.1",
"com.google.android.gms" % "play-services-analytics" % "9.2.1",
"com.android.support" % "design" % "24.1.0",
"com.android.support" % "gridlayout-v7" % "24.1.0",
"com.android.support" % "cardview-v7" % "24.1.0",
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.j256.ormlite" % "ormlite-android" % "4.48",
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2",
"com.android.support" % "design" % "24.2.1",
"com.android.support" % "gridlayout-v7" % "24.2.1",
"com.github.clans" % "fab" % "1.6.4",
"me.dm7.barcodescanner" % "zxing" % "1.8.4"
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.github.kevinsawicki" % "http-request" % "6.0",
"com.google.android.gms" % "play-services-ads" % "9.4.0",
"com.google.android.gms" % "play-services-analytics" % "9.4.0",
"com.j256.ormlite" % "ormlite-android" % "5.0",
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2",
"dnsjava" % "dnsjava" % "2.1.7",
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809",
"me.dm7.barcodescanner" % "zxing" % "1.9",
"net.glxn.qrgen" % "android" % "2.0"
)
proguardVersion in Android := "5.2.1"
@@ -55,12 +52,8 @@ proguardCache in Android := Seq()
proguardOptions in Android ++= Seq(
"-keep class com.github.shadowsocks.System { *; }",
"-dontwarn com.google.android.gms.**",
"-keep class com.google.android.gms.**",
"-dontnote com.google.android.gms.internal.**",
"-dontnote com.j256.ormlite.**",
"-dontnote me.dm7.barcodescannerview.**",
"-dontnote org.xbill.**",
"-dontwarn com.google.android.gms.internal.**",
"-dontwarn com.j256.ormlite.**",
"-dontwarn org.xbill.**")
lazy val nativeBuild = TaskKey[Unit]("native-build", "Build native executables")
+14 -8
View File
@@ -4,19 +4,25 @@ function try () {
"$@" || exit -1
}
try pushd src/main
# Build
#try $ANDROID_NDK_HOME/ndk-build clean
# Build native binaries
pushd src/main
try $ANDROID_NDK_HOME/ndk-build -j8
# copy executables
rm -rf assets/armeabi-v7a
rm -rf assets/x86
mkdir -p assets/armeabi-v7a
mkdir -p assets/x86
#copy executables
for app in pdnsd redsocks ss-local ss-tunnel tun2socks
do
rm -f assets/armeabi-v7a/$app
rm -f assets/x86/$app
echo $app
try mv libs/armeabi-v7a/$app assets/armeabi-v7a/
try mv libs/x86/$app assets/x86/
done
popd
try popd
# Build kcptun
pushd kcptun
try ./make.bash
popd
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
cd ${1}
travis-artifacts upload --path ${2} --target-path nightly
Submodule
+1
Submodule kcptun/go added at c3f75e654d
Submodule
+1
Submodule kcptun/kcptun added at e44d8c9041
+69
View File
@@ -0,0 +1,69 @@
#!/bin/bash
function try () {
"$@" || exit -1
}
[ -z "$ANDROID_NDK_HOME" ] && ANDROID_NDK_HOME=~/android-ndk-r12b
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DEPS=$DIR/.deps
ANDROID_ARM_TOOLCHAIN=$DEPS/android-toolchain-16-arm
ANDROID_X86_TOOLCHAIN=$DEPS/android-toolchain-16-x86
ANDROID_ARM_CC=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-gcc
ANDROID_ARM_STRIP=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-strip
ANDROID_X86_CC=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-gcc
ANDROID_X86_STRIP=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-strip
if [ ! -d "$DEPS" ]; then
mkdir -p $DEPS
fi
if [ ! -d "$ANDROID_ARM_TOOLCHAIN" ]; then
echo "Make standalone toolchain for ARM arch"
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm \
--api 16 --install-dir $ANDROID_ARM_TOOLCHAIN
fi
if [ ! -d "$ANDROID_X86_TOOLCHAIN" ]; then
echo "Make standalone toolchain for X86 arch"
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch x86 \
--api 16 --install-dir $ANDROID_X86_TOOLCHAIN
fi
if [ ! -d "$DIR/go/bin" ]; then
echo "Build the custom go"
pushd $DIR/go/src
try ./make.bash
popd
fi
export GOROOT=$DIR/go
export GOPATH=$DEPS/gopath
export GOBIN=$GOPATH/bin
mkdir -p $GOBIN
export PATH=$GOROOT/bin:$PATH
pushd kcptun/client
echo "Get dependences for kcptun"
go get -u github.com/xtaci/kcp-go
go get -u github.com/xtaci/smux
go get
echo "Cross compile kcptun for arm"
try env CGO_ENABLED=1 CC=$ANDROID_ARM_CC GOOS=android GOARCH=arm GOARM=7 go build -ldflags="-s -w"
try $ANDROID_ARM_STRIP client
try mv client $DIR/../src/main/assets/armeabi-v7a/kcptun
echo "Cross compile kcptun for x86"
try env CGO_ENABLED=1 CC=$ANDROID_X86_CC GOOS=android GOARCH=386 go build -ldflags="-s -w"
try $ANDROID_X86_STRIP client
try mv client $DIR/../src/main/assets/x86/kcptun
popd
echo "Successfully build kcptun"
-3
View File
@@ -1,3 +0,0 @@
key.alias: travis
key.store: travis.keystore
key.store.password: travis
+1 -1
View File
@@ -1,4 +1,4 @@
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.10")
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.18")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
+4 -4
View File
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks"
android:versionCode="142"
android:versionName="3.0.0">
android:versionCode="157"
android:versionName="3.1.5">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
@@ -19,7 +19,7 @@
android:required="false"/>
<uses-sdk
android:minSdkVersion="16"
android:minSdkVersion="19"
android:targetSdkVersion="24"/>
<application
@@ -109,7 +109,7 @@
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:taskAffinity=""
android:theme="@style/PopupTheme.QuickSwitch">
android:theme="@style/Theme.Material.Dialog">
<intent-filter>
<action android:name="com.github.shadowsocks.QUICK_SWITCH" />
<category android:name="android.intent.category.DEFAULT"/>
@@ -4,6 +4,7 @@ import com.github.shadowsocks.aidl.IShadowsocksServiceCallback;
interface IShadowsocksService {
int getState();
String getProfileName();
oneway void registerCallback(IShadowsocksServiceCallback cb);
oneway void unregisterCallback(IShadowsocksServiceCallback cb);
@@ -1,6 +1,6 @@
package com.github.shadowsocks.aidl;
interface IShadowsocksServiceCallback {
oneway void stateChanged(int state, String msg);
oneway void stateChanged(int state, String profileName, String msg);
oneway void trafficUpdated(long txRate, long rxRate, long txTotal, long rxTotal);
}
File diff suppressed because it is too large Load Diff
Binary file not shown.
+1
View File
@@ -296,6 +296,7 @@ along with this program. If not, see <a href="http://www.gnu.org/licenses/">http
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
<li>libev: <a href="https://github.com/shadowsocks/libevent/blob/shadowsocks-android/LICENSE">GPLv2</a></li>
<li>libevent: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libevent/LICENSE">BSD</a></li>
<li>kcptun: <a href="https://github.com/xtaci/kcptun/commits/master/LICENSE.md">MIT</a></li>
</ul>
</body>
Binary file not shown.
+72 -18
View File
@@ -222,7 +222,7 @@ include $(CLEAR_VARS)
LIBEVENT_SOURCES := \
buffer.c \
bufferevent.c bufferevent_filter.c \
bufferevent_openssl.c bufferevent_pair.c bufferevent_ratelim.c \
bufferevent_pair.c bufferevent_ratelim.c \
bufferevent_sock.c epoll.c \
epoll_sub.c evdns.c event.c \
event_tagging.c evmap.c \
@@ -236,7 +236,6 @@ LOCAL_MODULE := event
LOCAL_SRC_FILES := $(addprefix libevent/, $(LIBEVENT_SOURCES))
LOCAL_CFLAGS := -O2 -I$(LOCAL_PATH)/libevent \
-I$(LOCAL_PATH)/libevent/include \
-I$(LOCAL_PATH)/openssl/include
include $(BUILD_STATIC_LIBRARY)
@@ -372,7 +371,9 @@ PDNSD_SOURCES := $(wildcard $(LOCAL_PATH)/pdnsd/src/*.c)
LOCAL_MODULE := pdnsd
LOCAL_SRC_FILES := $(PDNSD_SOURCES:$(LOCAL_PATH)/%=%)
LOCAL_CFLAGS := -DANDROID -Wall -O2 -I$(LOCAL_PATH)/pdnsd \
-I$(LOCAL_PATH)/include/pdnsd
-I$(LOCAL_PATH)/include/pdnsd -I$(LOCAL_PATH)/libancillary
LOCAL_STATIC_LIBRARIES := libancillary
LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE)
@@ -382,25 +383,29 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c acl.c android.c
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c \
utils.c netutils.c json.c jconf.c acl.c http.c tls.c rule.c \
android.c
LOCAL_MODULE := ss-local
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_LOCAL \
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H \
-DUSE_CRYPTO_MBEDTLS -DANDROID -DHAVE_CONFIG_H \
-DCONNECT_IN_PROGRESS=EINPROGRESS \
-I$(LOCAL_PATH)/include/shadowsocks-libev \
-I$(LOCAL_PATH)/include \
-I$(LOCAL_PATH)/libancillary \
-I$(LOCAL_PATH)/openssl/include \
-I$(LOCAL_PATH)/mbedtls/include \
-I$(LOCAL_PATH)/pcre \
-I$(LOCAL_PATH)/shadowsocks-libev/libudns \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libev \
-I$(LOCAL_PATH)/include/shadowsocks-libev
-I$(LOCAL_PATH)/shadowsocks-libev/libev
LOCAL_STATIC_LIBRARIES := libev libcrypto libipset libcork libudns libsodium libancillary
LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libudns \
libsodium libancillary libpcre
LOCAL_LDLIBS := -llog
@@ -417,7 +422,7 @@ SHADOWSOCKS_SOURCES := tunnel.c cache.c udprelay.c encrypt.c utils.c netutils.c
LOCAL_MODULE := ss-tunnel
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_TUNNEL \
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H -DSSTUNNEL_JNI \
-DUSE_CRYPTO_MBEDTLS -DANDROID -DHAVE_CONFIG_H -DSSTUNNEL_JNI \
-DCONNECT_IN_PROGRESS=EINPROGRESS \
-I$(LOCAL_PATH)/libancillary \
-I$(LOCAL_PATH)/include \
@@ -425,11 +430,11 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_TUNNEL \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
-I$(LOCAL_PATH)/openssl/include \
-I$(LOCAL_PATH)/mbedtls/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libev \
-I$(LOCAL_PATH)/include/shadowsocks-libev
LOCAL_STATIC_LIBRARIES := libev libcrypto libsodium libcork libudns libancillary
LOCAL_STATIC_LIBRARIES := libev libmbedtls libsodium libcork libudns libancillary
LOCAL_LDLIBS := -llog
@@ -546,12 +551,61 @@ LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES))
include $(BUILD_EXECUTABLE)
# OpenSSL
openssl_subdirs := $(addprefix $(LOCAL_PATH)/openssl/,$(addsuffix /Android.mk, \
crypto \
ssl \
))
include $(openssl_subdirs)
########################################################
## mbed TLS
########################################################
include $(CLEAR_VARS)
LOCAL_MODULE := mbedtls
LOCAL_C_INCLUDES := $(LOCAL_PATH)/mbedtls/include
MBEDTLS_SOURCES := $(wildcard $(LOCAL_PATH)/mbedtls/library/*.c)
LOCAL_SRC_FILES := $(MBEDTLS_SOURCES:$(LOCAL_PATH)/%=%)
include $(BUILD_STATIC_LIBRARY)
########################################################
## pcre
########################################################
include $(CLEAR_VARS)
LOCAL_MODULE := pcre
LOCAL_CFLAGS += -DHAVE_CONFIG_H
LOCAL_C_INCLUDES := $(LOCAL_PATH)/pcre/dist $(LOCAL_PATH)/pcre
libpcre_src_files := \
pcre_chartables.c \
dist/pcre_byte_order.c \
dist/pcre_compile.c \
dist/pcre_config.c \
dist/pcre_dfa_exec.c \
dist/pcre_exec.c \
dist/pcre_fullinfo.c \
dist/pcre_get.c \
dist/pcre_globals.c \
dist/pcre_jit_compile.c \
dist/pcre_maketables.c \
dist/pcre_newline.c \
dist/pcre_ord2utf8.c \
dist/pcre_refcount.c \
dist/pcre_string_utils.c \
dist/pcre_study.c \
dist/pcre_tables.c \
dist/pcre_ucd.c \
dist/pcre_valid_utf8.c \
dist/pcre_version.c \
dist/pcre_xclass.c
LOCAL_SRC_FILES := $(addprefix pcre/, $(libpcre_src_files))
include $(BUILD_STATIC_LIBRARY)
# Import cpufeatures
$(call import-module,android/cpufeatures)
+37 -31
View File
@@ -26,7 +26,7 @@
/* #undef HAVE_CCCRYPTORCREATEWITHMODE */
/* Define to 1 if you have the `clock_gettime' function. */
#define HAVE_CLOCK_GETTIME 1
/* #undef HAVE_CLOCK_GETTIME */
/* Define to 1 to use the syscall interface for clock_gettime */
/* #undef HAVE_CLOCK_SYSCALL */
@@ -42,13 +42,13 @@
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the `epoll_ctl' function. */
#define HAVE_EPOLL_CTL 1
/* #undef HAVE_EPOLL_CTL */
/* Define to 1 if you have the `eventfd' function. */
#define HAVE_EVENTFD 1
/* #undef HAVE_EVENTFD */
/* Define to 1 if you have the `EVP_EncryptInit_ex' function. */
#define HAVE_EVP_ENCRYPTINIT_EX 1
/* #undef HAVE_EVP_ENCRYPTINIT_EX */
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
@@ -66,7 +66,7 @@
/* #undef HAVE_INET_NTOP */
/* Define to 1 if you have the `inotify_init' function. */
#define HAVE_INOTIFY_INIT 1
/* #undef HAVE_INOTIFY_INIT */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
@@ -75,7 +75,7 @@
#define HAVE_IPv6 1
/* Define to 1 if you have the `kqueue' function. */
/* #undef HAVE_KQUEUE */
#define HAVE_KQUEUE 1
/* Define to 1 if you have the <langinfo.h> header file. */
#define HAVE_LANGINFO_H 1
@@ -90,14 +90,14 @@
#define HAVE_LIMITS_H 1
/* Define to 1 if you have the <linux/if.h> header file. */
#define HAVE_LINUX_IF_H 1
/* #undef HAVE_LINUX_IF_H */
/* Define to 1 if you have the <linux/netfilter_ipv4.h> header file. */
#define HAVE_LINUX_NETFILTER_IPV4_H 1
/* #undef HAVE_LINUX_NETFILTER_IPV4_H */
/* Define to 1 if you have the <linux/netfilter_ipv6/ip6_tables.h> header
file. */
#define HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H 1
/* #undef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H */
/* Define to 1 if you have the <locale.h> header file. */
#define HAVE_LOCALE_H 1
@@ -124,25 +124,25 @@
#define HAVE_NET_IF_H 1
/* Define to 1 if you have the <openssl/engine.h> header file. */
#define HAVE_OPENSSL_ENGINE_H 1
/* #undef HAVE_OPENSSL_ENGINE_H */
/* Define to 1 if you have the <openssl/err.h> header file. */
#define HAVE_OPENSSL_ERR_H 1
/* #undef HAVE_OPENSSL_ERR_H */
/* Define to 1 if you have the <openssl/evp.h> header file. */
#define HAVE_OPENSSL_EVP_H 1
/* #undef HAVE_OPENSSL_EVP_H */
/* Define to 1 if you have the <openssl/pem.h> header file. */
#define HAVE_OPENSSL_PEM_H 1
/* #undef HAVE_OPENSSL_PEM_H */
/* Define to 1 if you have the <openssl/rand.h> header file. */
#define HAVE_OPENSSL_RAND_H 1
/* #undef HAVE_OPENSSL_RAND_H */
/* Define to 1 if you have the <openssl/rsa.h> header file. */
#define HAVE_OPENSSL_RSA_H 1
/* #undef HAVE_OPENSSL_RSA_H */
/* Define to 1 if you have the <openssl/sha.h> header file. */
#define HAVE_OPENSSL_SHA_H 1
/* #undef HAVE_OPENSSL_SHA_H */
/* Define to 1 if you have the `poll' function. */
#define HAVE_POLL 1
@@ -160,13 +160,13 @@
#define HAVE_PTHREAD_PRIO_INHERIT 1
/* Define to 1 if you have the `RAND_pseudo_bytes' function. */
#define HAVE_RAND_PSEUDO_BYTES 1
/* #undef HAVE_RAND_PSEUDO_BYTES */
/* Define to 1 if you have the 'select' function. */
#define HAVE_SELECT 1
/* Define to 1 if you have the `setresuid' function. */
#define HAVE_SETRESUID 1
/* #undef HAVE_SETRESUID */
/* Define to 1 if you have the `setreuid' function. */
#define HAVE_SETREUID 1
@@ -175,7 +175,7 @@
#define HAVE_SETRLIMIT 1
/* Define to 1 if you have the `signalfd' function. */
#define HAVE_SIGNALFD 1
/* #undef HAVE_SIGNALFD */
/* Define to 1 if you have the `socket' function. */
#define HAVE_SOCKET 1
@@ -196,16 +196,16 @@
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/epoll.h> header file. */
#define HAVE_SYS_EPOLL_H 1
/* #undef HAVE_SYS_EPOLL_H */
/* Define to 1 if you have the <sys/eventfd.h> header file. */
#define HAVE_SYS_EVENTFD_H 1
/* #undef HAVE_SYS_EVENTFD_H */
/* Define to 1 if you have the <sys/event.h> header file. */
/* #undef HAVE_SYS_EVENT_H */
#define HAVE_SYS_EVENT_H 1
/* Define to 1 if you have the <sys/inotify.h> header file. */
#define HAVE_SYS_INOTIFY_H 1
/* #undef HAVE_SYS_INOTIFY_H */
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#define HAVE_SYS_IOCTL_H 1
@@ -214,7 +214,7 @@
#define HAVE_SYS_SELECT_H 1
/* Define to 1 if you have the <sys/signalfd.h> header file. */
#define HAVE_SYS_SIGNALFD_H 1
/* #undef HAVE_SYS_SIGNALFD_H */
/* Define to 1 if you have the <sys/socket.h> header file. */
#define HAVE_SYS_SOCKET_H 1
@@ -253,10 +253,10 @@
/* #undef HAVE_WS2TCPIP_H */
/* have zlib compression support */
#define HAVE_ZLIB 1
/* #undef HAVE_ZLIB */
/* Define to 1 if you have the <zlib.h> header file. */
#define HAVE_ZLIB_H 1
/* #undef HAVE_ZLIB_H */
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
@@ -275,7 +275,7 @@
#define PACKAGE_NAME "shadowsocks-libev"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "shadowsocks-libev 2.4.7"
#define PACKAGE_STRING "shadowsocks-libev 2.4.8"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "shadowsocks-libev"
@@ -284,7 +284,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "2.4.7"
#define PACKAGE_VERSION "2.4.8"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
@@ -315,10 +315,10 @@
/* #undef USE_CRYPTO_APPLECC */
/* Use mbed TLS library */
/* #undef USE_CRYPTO_MBEDTLS */
#define USE_CRYPTO_MBEDTLS 1
/* Use OpenSSL library */
#define USE_CRYPTO_OPENSSL 1
/* #undef USE_CRYPTO_OPENSSL */
/* Use PolarSSL library */
/* #undef USE_CRYPTO_POLARSSL */
@@ -346,7 +346,7 @@
/* Version number of package */
#define VERSION "2.4.7"
#define VERSION "2.4.8"
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
@@ -417,3 +417,9 @@
/* Define as `fork' if `vfork' does not work. */
/* #undef vfork */
/* Define to 1 if you have the <pcre.h> header file. */
#define HAVE_PCRE_H 1
/* Define to 1 if you have the <pcre/pcre.h> header file. */
/* #undef HAVE_PCRE_PCRE_H */
+1
Submodule src/main/jni/pcre added at 0ea9883633
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
+1
View File
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
+1
View File
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
+1
View File
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -1,3 +1,4 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
+2 -1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -11,4 +12,4 @@
<path
android:fillColor="#fff"
android:pathData="M 9.45 17.56 L 9.46 22 L 12.09 18.41 L 9.45 17.56 L 9.45 17.56 Z" />
</vector>
</vector>
+2 -1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -11,4 +12,4 @@
<path
android:fillColor="#fff"
android:pathData="M 9.45 17.56 L 12.09 18.41 L 9.46 22 L 9.45 17.56 Z" />
</vector>
</vector>
+2 -1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
@@ -22,4 +23,4 @@
<path
android:fillColor="#fff"
android:pathData="M8.56,8.56L0,12.8l6.83,2.57,4.64-3.9ZM6.44,13.08L5,12.55l5-2.47Z" />
</vector>
</vector>
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="@string/stat" android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary" android:ellipsize="marquee"
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView android:id="@+id/connection_test" android:gravity="end"
android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"
android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/>
</LinearLayout>
+4 -4
View File
@@ -18,8 +18,8 @@
android:text="@string/on"
android:textSize="18sp"
android:textColor="?android:attr/textColorSecondary"
android:paddingLeft="60dp"
android:paddingRight="8dp"
android:paddingStart="60dp"
android:paddingEnd="8dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:layout_width="match_parent"
@@ -30,8 +30,8 @@
android:text="@string/bypass_apps"
android:textSize="18sp"
android:textColor="?android:attr/textColorSecondary"
android:paddingLeft="60dp"
android:paddingRight="8dp"
android:paddingStart="60dp"
android:paddingEnd="8dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:layout_width="match_parent"
+3 -4
View File
@@ -11,8 +11,8 @@
android:layout_width="60dp"
android:layout_height="48dp"
android:scaleType="fitCenter"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"/>
@@ -28,7 +28,6 @@
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"/>
android:layout_marginEnd="8dp"/>
</LinearLayout>
+26 -9
View File
@@ -10,17 +10,35 @@
<LinearLayout android:id="@+id/stat" android:elevation="4dp" android:visibility="gone"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight" android:gravity="center_vertical"
android:paddingStart="16dip" android:paddingEnd="88dip" android:focusable="true"
android:paddingLeft="16dip" android:paddingRight="88dip"
android:paddingStart="16dip" android:paddingEnd="16dip" android:focusable="true"
android:paddingTop="16dip" android:paddingBottom="16dip"
android:background="@drawable/background_stat" android:orientation="vertical">
<include layout="@layout/layout_main_stat_title"/>
android:background="@drawable/background_stat" android:orientation="vertical"
android:nextFocusRight="@+id/fab">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:text="@string/stat"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="marquee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@+id/connection_test"
android:gravity="end"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
<android.support.v7.widget.GridLayout android:layout_width="match_parent"
android:layout_height="wrap_content" app:columnCount="4">
android:layout_height="wrap_content"
android:paddingEnd="72dip"
app:columnCount="4">
<TextView app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
android:ellipsize="marquee" android:text="@string/sent"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="24dp" android:layout_marginEnd="24dp" android:gravity="end"/>
android:layout_marginEnd="24dp"
android:gravity="end"/>
<TextView app:layout_column="1" android:id="@+id/tx" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content"
@@ -35,7 +53,8 @@
app:layout_gravity="fill_horizontal" android:gravity="end"/>
<TextView app:layout_row="1" app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="24dp" android:layout_marginEnd="24dp" android:gravity="end"
android:layout_marginEnd="24dp"
android:gravity="end"
android:ellipsize="marquee" android:text="@string/received"/>
<TextView app:layout_row="1" app:layout_column="1" android:id="@+id/rx" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
@@ -58,9 +77,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:elevation="6dp"
android:layout_alignBottom="@+id/title_bar"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="@dimen/fab_margin_bottom">
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content"
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="@string/stat" android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary" android:ellipsize="marquee"
android:layout_width="match_parent" android:layout_height="wrap_content"/>
<TextView android:id="@+id/connection_test"
android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"
android:layout_width="match_parent" android:layout_height="wrap_content"/>
</LinearLayout>
-1
View File
@@ -16,7 +16,6 @@
android:layout_height="match_parent"
android:layout_gravity="bottom|end"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
app:menu_colorNormal="@color/material_accent_500"
app:menu_colorPressed="@color/material_accent_700"
@@ -8,8 +8,6 @@
android:background="?android:attr/selectableItemBackground"
android:paddingStart="20dp"
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingLeft="20dp"
android:paddingRight="?attr/dialogPreferredPadding"
android:focusable="true"
android:nextFocusRight="@+id/share">
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
@@ -23,7 +21,6 @@
android:textColor="?android:attr/textColorAlertDialogListItem"
android:gravity="center_vertical"
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
android:drawableLeft="?android:attr/listChoiceIndicatorSingle"
android:drawablePadding="20dp"
android:maxLines="4"
android:scrollHorizontally="false" />
+9 -19
View File
@@ -1,24 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_margin="8dp"
android:layout_gravity="center_vertical"
app:cardElevation="5dp"
app:cardBackgroundColor="#EEEEEE">
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
<include layout="@layout/toolbar_light_dark"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/profilesList"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<include layout="@layout/toolbar_light_dark"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/profilesList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.v7.widget.CardView>
android:layout_height="wrap_content"/>
</LinearLayout>
+10 -2
View File
@@ -46,6 +46,13 @@
<string name="auto_set_gfwlist">CHN Маршрут</string>
<string name="auto_set_gfwlist_summary">Игнорировать все сайты находящиеся в Китае</string>
<string name="route_list">Маршрут</string>
<string-array name="route_entry">
<item>Все</item>
<item>Обход LAN</item>
<item>Обходной Китай</item>
<item>Обход LAN и Китай</item>
<item>Список GFW</item>
</string-array>
<string name="proxied_apps">Прокси для выбранных приложений</string>
<string name="proxied_apps_summary">Установить прокси для выбранных приложений, требует режим NAT под Android 4.x</string>
<string name="on">Вкл</string>
@@ -53,6 +60,8 @@
<string name="bypass_apps_summary">Включите эту опцию для работы выбранных приложений в обход прокси</string>
<string name="auto_connect">Авто Подключение</string>
<string name="auto_connect_summary">Запускать Shadowsocks при включении</string>
<string name="tcp_fastopen_summary">Переключение требует наличия ROOT прав</string>
<string name="tcp_fastopen_summary_unsupported">Неподдерживаемая версия ядра: %s &lt; 3.7.1</string>
<string name="udp_dns">Проброс UDP</string>
<string name="udp_dns_summary">Пробрасывать UDP пакеты на удалённый сервер</string>
@@ -92,8 +101,7 @@
<string name="share_message_nfc_disabled">Сканировать QR код\nAndroid Beam отключен</string>
<string name="turn_on_nfc">Включить NFC &amp; Android Beam</string>
<string name="flush_dnscache">Очистить кэш DNS</string>
<string name="flush_dnscache_summary">Работает только в режиме NAT</string>
<string name="flush_dnscache_summary_root">Работает только в режиме NAT. Требует наличия ROOT прав.</string>
<string name="flush_dnscache_summary">Работает только в режиме NAT. Требует наличия ROOT прав.</string>
<string name="flush_dnscache_no_root">Очистка кэша DNS не работает без налиичя ROOT прав</string>
<string name="flushing">Очистка</string>
<string name="add_profile">Добавить Профиль</string>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Material.Dialog" parent="Theme.Material.DialogBase"/>
<style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.AlertBase"/>
</resources>
+5 -2
View File
@@ -32,6 +32,8 @@
<string name="bypass_apps_summary">启用该选项,以使所选应用程序的流量不经过代理</string>
<string name="auto_connect">自动连接</string>
<string name="auto_connect_summary">随系统启动后台服务</string>
<string name="tcp_fastopen_summary">切换需要 ROOT 权限</string>
<string name="tcp_fastopen_summary_unsupported">不支持的内核版本:%s &lt; 3.7.1</string>
<string name="forward_success">后台服务已开始运行。</string>
<string name="service_failed">无法连接远程服务器</string>
<string name="invalid_server">服务器名无效</string>
@@ -58,8 +60,7 @@
<string name="about">关于</string>
<string name="about_title">影梭(shadowsocks)%s</string>
<string name="flush_dnscache">刷新 DNS 缓存</string>
<string name="flush_dnscache_summary">只需在 NAT 模式下使用</string>
<string name="flush_dnscache_summary_root">只需在 NAT 模式下使用,需要 ROOT 权限</string>
<string name="flush_dnscache_summary">只需在 NAT 模式下使用,需要 ROOT 权限</string>
<string name="flush_dnscache_no_root">在没有 ROOT 权限的情况下无法刷新 DNS 缓存</string>
<string name="flushing">刷新中……</string>
<string name="share">分享</string>
@@ -109,6 +110,8 @@
<item>绕过局域网地址</item>
<item>绕过中国大陆地址</item>
<item>绕过局域网及中国大陆地址</item>
<item>仅代理中国大陆无法访问的地址</item>
<item>仅代理中国大陆地址</item>
</string-array>
<string name="action_apply_all">应用设置到所有配置文件</string>
+5 -2
View File
@@ -33,6 +33,8 @@
<string name="bypass_apps_summary">啟用此選項,會略過已選擇的應用程式</string>
<string name="auto_connect">自動連線</string>
<string name="auto_connect_summary">在裝置啟動時啟用 Shadowsocks</string>
<string name="tcp_fastopen_summary">切換需要 ROOT 權限</string>
<string name="tcp_fastopen_summary_unsupported">不支持的內核版本:%s &lt; 3.7.1</string>
<string name="forward_success">Shadowsocks 已啟動。</string>
<string name="service_failed">連線至遠端伺服器失敗</string>
<string name="invalid_server">伺服器名稱無效</string>
@@ -59,8 +61,7 @@
<string name="about">關於</string>
<string name="about_title">Shadowsocks %s</string>
<string name="flush_dnscache">清除 DNS 快取</string>
<string name="flush_dnscache_summary">僅在 NAT 模式可用</string>
<string name="flush_dnscache_summary_root">僅在 NAT 模式可用,需要 ROOT 權限</string>
<string name="flush_dnscache_summary">僅在 NAT 模式可用,需要 ROOT 權限</string>
<string name="flush_dnscache_no_root">在無 ROOT 權限時清除 DNS 快取無法進行</string>
<string name="flushing">清除中……</string>
<string name="share">分享</string>
@@ -111,6 +112,8 @@
<item>略過區域網路位址</item>
<item>略過中國大陸位址</item>
<item>略過區域網路及中國大陸位址</item>
<item>僅代理大陸屏蔽位址</item>
<item>僅代理大陸位址</item>
</string-array>
<string name="action_apply_all">應用設置到所有配置文件</string>
+8 -17
View File
@@ -7,15 +7,13 @@
<item>AES-128-CFB</item>
<item>AES-192-CFB</item>
<item>AES-256-CFB</item>
<item>AES-128-CTR</item>
<item>AES-192-CTR</item>
<item>AES-256-CTR</item>
<item>BF-CFB</item>
<item>CAMELLIA-128-CFB</item>
<item>CAMELLIA-192-CFB</item>
<item>CAMELLIA-256-CFB</item>
<item>CAST5-CFB</item>
<item>DES-CFB</item>
<item>IDEA-CFB</item>
<item>RC2-CFB</item>
<item>SEED-CFB</item>
<item>SALSA20</item>
<item>CHACHA20</item>
<item>CHACHA20-IETF</item>
@@ -28,32 +26,25 @@
<item>aes-128-cfb</item>
<item>aes-192-cfb</item>
<item>aes-256-cfb</item>
<item>aes-128-ctr</item>
<item>aes-192-ctr</item>
<item>aes-256-ctr</item>
<item>bf-cfb</item>
<item>camellia-128-cfb</item>
<item>camellia-192-cfb</item>
<item>camellia-256-cfb</item>
<item>cast5-cfb</item>
<item>des-cfb</item>
<item>idea-cfb</item>
<item>rc2-cfb</item>
<item>seed-cfb</item>
<item>salsa20</item>
<item>chacha20</item>
<item>chacha20-ietf</item>
</string-array>
<string-array name="route_entry">
<item>All</item>
<item>Bypass LAN</item>
<item>Bypass China</item>
<item>Bypass LAN &amp; China</item>
</string-array>
<string-array name="route_value">
<item>all</item>
<item>bypass-lan</item>
<item>bypass-china</item>
<item>bypass-lan-china</item>
<item>gfwlist</item>
<item>china-list</item>
</string-array>
<string-array name="private_route">
+11 -2
View File
@@ -52,6 +52,14 @@
<string name="auto_set_gfwlist_summary">Bypass all sites located in China
</string>
<string name="route_list">Route</string>
<string-array name="route_entry">
<item>All</item>
<item>Bypass LAN</item>
<item>Bypass China</item>
<item>Bypass LAN &amp; China</item>
<item>GFW List</item>
<item>China List</item>
</string-array>
<string name="proxied_apps">Per-App Proxy</string>
<string name="proxied_apps_summary">Set proxy for selected apps, requires NAT mode under Android 4.x</string>
<string name="on">On</string>
@@ -59,6 +67,8 @@
<string name="bypass_apps_summary">Enable this option to bypass selected apps</string>
<string name="auto_connect">Auto Connect</string>
<string name="auto_connect_summary">Enable Shadowsocks on startup</string>
<string name="tcp_fastopen_summary">Toggling requires ROOT permission</string>
<string name="tcp_fastopen_summary_unsupported">Unsupported kernel version: %s &lt; 3.7.1</string>
<string name="udp_dns">UDP Forwarding</string>
<string name="udp_dns_summary">Forward UDP packets to remote</string>
@@ -98,8 +108,7 @@
<string name="share_message_nfc_disabled">Scan QR code\nAndroid Beam is disabled</string>
<string name="turn_on_nfc">Turn on NFC &amp; Android Beam</string>
<string name="flush_dnscache">Flush DNS cache</string>
<string name="flush_dnscache_summary">Only useful in NAT mode</string>
<string name="flush_dnscache_summary_root">Only useful in NAT mode. Requires ROOT permission.</string>
<string name="flush_dnscache_summary">Only useful in NAT mode. Requires ROOT permission.</string>
<string name="flush_dnscache_no_root">Flush DNS cache cannot proceed without ROOT permission</string>
<string name="flushing">Flushing</string>
<string name="add_profile">Add Profile</string>
+2 -30
View File
@@ -7,8 +7,7 @@
</style>
<style name="Theme.Material" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:alertDialogTheme">@style/Theme.Material.Dialog.Alert</item>
<item name="android:dialogTheme">@style/Theme.Material.Dialog</item>
<!--suppress NewApi -->
<item name="android:navigationBarColor">@color/material_accent_700</item>
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorButtonNormal">@color/material_accent_500</item>
@@ -16,38 +15,11 @@
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="Theme.Material.DialogBase" parent="Theme.AppCompat.Light.Dialog">
<style name="Theme.Material.Dialog" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="Theme.Material.Dialog.AlertBase" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="Theme.Material.Dialog" parent="Theme.Material.DialogBase">
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.AlertBase">
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<style name="PopupTheme" parent="Theme.Material.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowSoftInputMode">stateAlwaysHidden</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<style name="PopupTheme.QuickSwitch" parent="PopupTheme">
<item name="windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
</style>
</resources>
+7 -2
View File
@@ -94,7 +94,7 @@
android:summary="@string/kcp_port_summary"
android:title="@string/kcp_port"/>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
<com.github.shadowsocks.preferences.KcpCliPreference
android:key="kcpcli"
android:persistent="false"
android:summary="@string/kcpcli_summary"
@@ -110,6 +110,11 @@
android:summary="@string/auto_connect_summary"
android:title="@string/auto_connect"/>
<SwitchPreference
android:key="tcp_fastopen"
android:summary="@string/tcp_fastopen_summary"
android:title="TCP Fast Open"/>
<SwitchPreference android:key="isNAT"
android:title="@string/nat"
android:summary="@string/nat_summary"/>
@@ -117,7 +122,7 @@
android:summary="@string/recovery_summary"/>
<Preference android:key="flush_dnscache"
android:title="@string/flush_dnscache"
android:summary="@string/flush_dnscache_summary_root"/>
android:summary="@string/flush_dnscache_summary"/>
<Preference android:key="about" android:title="@string/about"/>
</PreferenceCategory>
@@ -46,14 +46,13 @@ import android.content._
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.{Bundle, Handler}
import android.preference.PreferenceManager
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar}
import android.view._
import android.widget._
import com.github.shadowsocks.utils.{Key, Utils}
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.utils.{Key, Utils}
import scala.collection.JavaConversions._
import scala.collection.mutable
@@ -143,7 +142,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
private var loadingView: View = _
private val appsLoading = new AtomicBoolean
private var handler: Handler = _
private val profile = app.currentProfile.get
private val profile = app.currentProfile.orNull
private def initProxiedApps(str: String = profile.individual) = proxiedApps = str.split('\n').to[mutable.HashSet]
@@ -213,6 +212,8 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
protected override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
if (profile == null) finish()
handler = new Handler()
this.setContentView(R.layout.layout_apps)
@@ -39,10 +39,12 @@
package com.github.shadowsocks
import java.io.IOException
import java.util.{Timer, TimerTask}
import android.app.Service
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
import android.net.ConnectivityManager
import android.os.{Handler, RemoteCallbackList}
import android.text.TextUtils
import android.util.Log
@@ -58,6 +60,9 @@ trait BaseService extends Service {
@volatile private var state = State.STOPPED
@volatile protected var profile: Profile = _
case class NameNotResolvedException() extends IOException
case class KcpcliParseException(cause: Throwable) extends Exception(cause)
var timer: Timer = _
var trafficMonitorThread: TrafficMonitorThread = _
@@ -71,11 +76,23 @@ trait BaseService extends Service {
}
var closeReceiverRegistered: Boolean = _
var kcptunProcess: GuardedProcess = _
private val networkReceiver: BroadcastReceiver = (context: Context, intent: Intent) => {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager]
val activeNetwork = cm.getActiveNetworkInfo()
val isConnected = activeNetwork != null && activeNetwork.isConnected()
if (isConnected && profile.kcp && kcptunProcess != null) kcptunProcess.restart()
}
var networkReceiverRegistered: Boolean = _
val binder = new IShadowsocksService.Stub {
override def getState: Int = {
state
}
override def getProfileName: String = if (profile == null) null else profile.name
override def unregisterCallback(cb: IShadowsocksServiceCallback) {
if (cb != null && callbacks.unregister(cb)) {
callbacksCount -= 1
@@ -124,7 +141,7 @@ trait BaseService extends Service {
false
} else true
def connect() = if (profile.host == "198.199.101.152") try {
def connect() = if (profile.host == "198.199.101.152") {
val holder = app.containerHolder
val container = holder.getContainer
val url = container.getString("proxy_url")
@@ -141,8 +158,6 @@ trait BaseService extends Service {
profile.remotePort = proxy(1).trim.toInt
profile.password = proxy(2).trim
profile.method = proxy(3).trim
} catch {
case ex: Exception => stopRunner(true, getString(R.string.service_failed))
}
def startRunner(profile: Profile) {
@@ -162,13 +177,29 @@ trait BaseService extends Service {
closeReceiverRegistered = true
}
if (profile.kcp && !networkReceiverRegistered) {
// register network change receiver
val filter = new IntentFilter()
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
registerReceiver(networkReceiver, filter)
networkReceiverRegistered = true
}
app.track(getClass.getSimpleName, "start")
changeState(State.CONNECTING)
if (profile.isMethodUnsafe) handler.post(() => Toast.makeText(this, R.string.method_unsafe, Toast.LENGTH_LONG).show)
Utils.ThrowableFuture(connect)
Utils.ThrowableFuture(try connect catch {
case _: NameNotResolvedException => stopRunner(true, getString(R.string.invalid_server))
case exc: KcpcliParseException =>
stopRunner(true, getString(R.string.service_failed) + ": " + exc.cause.getMessage)
case exc: Throwable =>
stopRunner(true, getString(R.string.service_failed) + ": " + exc.getMessage)
exc.printStackTrace()
app.track(exc)
})
}
def stopRunner(stopService: Boolean, msg: String = null) {
@@ -177,6 +208,10 @@ trait BaseService extends Service {
unregisterReceiver(closeReceiver)
closeReceiverRegistered = false
}
if (networkReceiverRegistered) {
unregisterReceiver(networkReceiver)
networkReceiverRegistered = false
}
// Make sure update total traffic when stopping the runner
updateTrafficTotal(TrafficMonitor.txTotal, TrafficMonitor.rxTotal)
@@ -249,7 +284,7 @@ trait BaseService extends Service {
val n = callbacks.beginBroadcast()
for (i <- 0 until n) {
try {
callbacks.getBroadcastItem(i).stateChanged(s, msg)
callbacks.getBroadcastItem(i).stateChanged(s, binder.getProfileName, msg)
} catch {
case _: Exception => // Ignore
}
@@ -39,23 +39,41 @@
package com.github.shadowsocks
import java.io.{IOException, InputStream, OutputStream}
import java.io._
import java.lang.System.currentTimeMillis
import java.util.concurrent.Semaphore
import android.util.Log
import scala.collection.JavaConversions._
import scala.collection.immutable.Stream
import scala.util.control.Exception._
class StreamLogger(is: InputStream, tag: String) extends Thread {
def withCloseable[T <: Closeable, R](t: T)(f: T => R): R = {
allCatch.andFinally{t.close} apply { f(t) }
}
override def run() {
withCloseable(new BufferedReader(new InputStreamReader(is))) {
br => try Stream.continually(br.readLine()).takeWhile(_ != null).foreach(Log.i(tag, _)) catch {
case ignore: IOException =>
}
}
}
}
/**
* @author ayanamist@gmail.com
*/
class GuardedProcess(cmd: Seq[String]) extends Process {
class GuardedProcess(cmd: Seq[String]) {
private val TAG = classOf[GuardedProcess].getSimpleName
@volatile private var guardThread: Thread = _
@volatile private var isDestroyed: Boolean = _
@volatile private var process: Process = _
@volatile private var isRestart = false
def start(onRestartCallback: () => Unit = null): GuardedProcess = {
val semaphore = new Semaphore(1)
@@ -71,15 +89,25 @@ class GuardedProcess(cmd: Seq[String]) extends Process {
process = new ProcessBuilder(cmd).redirectErrorStream(true).start
val is = process.getInputStream
new StreamLogger(is, TAG).start
if (callback == null) callback = onRestartCallback else callback()
semaphore.release
process.waitFor
if (currentTimeMillis - startTime < 1000) {
Log.w(TAG, "process exit too fast, stop guard: " + cmd)
isDestroyed = true
this.synchronized {
if (isRestart) {
isRestart = false
} else {
if (currentTimeMillis - startTime < 1000) {
Log.w(TAG, "process exit too fast, stop guard: " + cmd)
isDestroyed = true
}
}
}
}
} catch {
case ignored: InterruptedException =>
@@ -108,10 +136,12 @@ class GuardedProcess(cmd: Seq[String]) extends Process {
}
}
def exitValue: Int = throw new UnsupportedOperationException
def getErrorStream: InputStream = throw new UnsupportedOperationException
def getInputStream: InputStream = throw new UnsupportedOperationException
def getOutputStream: OutputStream = throw new UnsupportedOperationException
def restart() {
this.synchronized {
isRestart = true
process.destroy()
}
}
@throws(classOf[InterruptedException])
def waitFor = {
@@ -114,12 +114,14 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
finish
}
def onKey(v: View, keyCode: Int, event: KeyEvent) = if (event.getAction == KeyEvent.ACTION_UP) keyCode match {
def onKey(v: View, keyCode: Int, event: KeyEvent) = if (event.getAction == KeyEvent.ACTION_DOWN) keyCode match {
case KeyEvent.KEYCODE_DPAD_LEFT =>
val index = getAdapterPosition
profilesAdapter.remove(index)
undoManager.remove(index, item)
true
if (index >= 0) {
profilesAdapter.remove(index)
undoManager.remove(index, item)
true
} else false
case _ => false
} else false
}
@@ -233,7 +235,7 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
}).attachToRecyclerView(profilesList)
attachService(new IShadowsocksServiceCallback.Stub {
def stateChanged(state: Int, msg: String) = () // ignore
def stateChanged(state: Int, profileName: String, msg: String) = () // ignore
def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) =
if (selectedItem != null) selectedItem.updateText(txTotal, rxTotal)
})
@@ -274,7 +276,7 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
updateNfcState()
}
override def onNewIntent(intent: Intent): Unit ={
override def onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleShareIntent(intent)
}
@@ -78,6 +78,7 @@ object Typefaces {
} catch {
case e: Exception =>
Log.e(TAG, "Could not get typeface '" + assetPath + "' because " + e.getMessage)
app.track(e)
return null
}
}
@@ -110,7 +111,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
// Services
private val callback = new IShadowsocksServiceCallback.Stub {
def stateChanged(s: Int, m: String) {
def stateChanged(s: Int, profileName: String, m: String) {
handler.post(() => {
s match {
case State.CONNECTING =>
@@ -185,8 +186,8 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
snackbar.show
}
if (!app.settings.getBoolean(app.getVersionName, false)) {
app.editor.putBoolean(app.getVersionName, true).apply()
if (app.settings.getInt(Key.currentVersionCode, -1) != BuildConfig.VERSION_CODE) {
app.editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
recovery()
@@ -246,6 +247,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
} catch {
case e: IOException =>
Log.e(TAG, e.getMessage)
app.track(e)
}
if (files != null) {
for (file <- files) {
@@ -267,6 +269,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
} catch {
case ex: Exception =>
Log.e(TAG, ex.getMessage)
app.track(ex)
}
}
}
@@ -500,11 +503,8 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
clearDialog()
}
private var _isDestroyed: Boolean = _
override def isDestroyed = if (Build.VERSION.SDK_INT >= 17) super.isDestroyed else _isDestroyed
override def onDestroy() {
super.onDestroy()
_isDestroyed = true
detachService()
new BackupManager(this).dataChanged()
handler.removeCallbacksAndMessages(null)
@@ -514,6 +514,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
crashRecovery()
copyAssets(System.getABI)
copyAssets("acl")
val ab = new ArrayBuffer[String]
for (executable <- EXECUTABLES) {
@@ -43,12 +43,11 @@ import java.util
import java.util.concurrent.TimeUnit
import android.app.Application
import android.content.pm.PackageManager
import android.preference.PreferenceManager
import android.support.v7.app.AppCompatDelegate
import com.github.shadowsocks.database.{DBHelper, ProfileManager}
import com.github.shadowsocks.utils.{Key, Utils}
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders}
import com.github.shadowsocks.utils.{Key, Utils, TcpFastOpen}
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders, StandardExceptionParser}
import com.google.android.gms.common.api.ResultCallback
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
import com.j256.ormlite.logger.LocalLog
@@ -70,17 +69,14 @@ class ShadowsocksApplication extends Application {
def isNatEnabled = settings.getBoolean(Key.isNAT, false)
def isVpnEnabled = !isNatEnabled
def getVersionName = try {
getPackageManager.getPackageInfo(getPackageName, 0).versionName
} catch {
case _: PackageManager.NameNotFoundException => "Package name not found"
case _: Throwable => null
}
// send event
def track(category: String, action: String) = tracker.send(new HitBuilders.EventBuilder()
.setAction(action)
.setLabel(getVersionName)
.setLabel(BuildConfig.VERSION_NAME)
.build())
def track(t: Throwable) = tracker.send(new HitBuilders.ExceptionBuilder()
.setDescription(new StandardExceptionParser(this, null).getDescription(Thread.currentThread.getName, t))
.setFatal(false)
.build())
def profileId = settings.getInt(Key.id, -1)
@@ -115,6 +111,8 @@ class ShadowsocksApplication extends Application {
}
}
pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
TcpFastOpen.enabled(settings.getBoolean(Key.tfo, false))
}
def refreshContainerHolder {
@@ -40,7 +40,6 @@
package com.github.shadowsocks
import java.io.File
import java.lang.Process
import java.net.{Inet6Address, InetAddress}
import java.util.Locale
@@ -65,23 +64,24 @@ class ShadowsocksNatService extends BaseService {
private var notification: ShadowsocksNotification = _
val myUid = android.os.Process.myUid()
var kcptunProcess: Process = _
var sslocalProcess: Process = _
var sstunnelProcess: Process = _
var redsocksProcess: Process = _
var pdnsdProcess: Process = _
var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _
var redsocksProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _
var su: Shell.Interactive = _
def startShadowsocksDaemon() {
if (profile.route != Route.ALL) {
val acl: Array[Array[String]] = profile.route match {
case Route.BYPASS_LAN => Array(getResources.getStringArray(R.array.private_route))
case Route.BYPASS_CHN => Array(getResources.getStringArray(R.array.chn_route))
if (profile.route != Route.ALL && profile.route != Route.GFWLIST) {
val acl: Array[String] = profile.route match {
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route)
case Route.BYPASS_LAN_CHN =>
Array(getResources.getStringArray(R.array.private_route), getResources.getStringArray(R.array.chn_route))
getResources.getStringArray(R.array.private_route) ++ getResources.getStringArray(R.array.chn_route)
case Route.CHINALIST =>
Array("[bypass_all]", "[white_list]") ++ getResources.getStringArray(R.array.chn_route)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => {
acl.flatten.foreach(p.println)
acl.foreach(p.println)
})
}
@@ -98,8 +98,7 @@ class ShadowsocksNatService extends BaseService {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-local"
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/ss-local"
, "-b" , "127.0.0.1"
, "-t" , "600"
, "-P", getApplicationInfo.dataDir
@@ -107,9 +106,14 @@ class ShadowsocksNatService extends BaseService {
if (profile.auth) cmd += "-A"
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
if (profile.route != Route.ALL) {
cmd += "--acl"
cmd += (getApplicationInfo.dataDir + "/acl.list")
if (profile.route == Route.GFWLIST)
cmd += (getApplicationInfo.dataDir + "/gfwlist.acl")
else
cmd += (getApplicationInfo.dataDir + "/acl.list")
}
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
@@ -117,16 +121,18 @@ class ShadowsocksNatService extends BaseService {
}
def startKcptunDaemon() {
if (profile.kcpcli == null) profile.kcpcli = ""
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/kcptun"
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/kcptun"
, "-r", profile.host + ":" + profile.kcpPort
, "-l", "127.0.0.1:" + (profile.localPort + 90)
, profile.kcpcli)
, "-l", "127.0.0.1:" + (profile.localPort + 90))
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
case exc: Exception => throw KcpcliParseException(exc)
}
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
kcptunProcess = new GuardedProcess(cmd.mkString(" ").split(" ").toSeq).start()
kcptunProcess = new GuardedProcess(cmd).start()
}
def startDNSTunnel() {
@@ -137,8 +143,7 @@ class ShadowsocksNatService extends BaseService {
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-tunnel"
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/ss-tunnel"
, "-u"
, "-t" , "10"
, "-b" , "127.0.0.1"
@@ -166,8 +171,7 @@ class ShadowsocksNatService extends BaseService {
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => {
p.println(conf)
})
val cmdBuf = new ArrayBuffer[String]
cmdBuf += (getApplicationInfo.dataDir + "/ss-tunnel"
val cmdBuf = ArrayBuffer[String](getApplicationInfo.dataDir + "/ss-tunnel"
, "-t" , "10"
, "-b" , "127.0.0.1"
, "-l" , (profile.localPort + 63).toString
@@ -185,38 +189,48 @@ class ShadowsocksNatService extends BaseService {
def startDnsDaemon() {
val conf = if (profile.route == Route.BYPASS_CHN || profile.route == Route.BYPASS_LAN_CHN) {
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, getBlackList, profile.localPort + 63, "")
} else {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, "")
val conf = profile.route match {
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN => {
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, getBlackList, profile.localPort + 63, "")
}
case Route.GFWLIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, "1.2.4.8, 208.67.222.222", profile.localPort + 63, "")
}
case Route.CHINALIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, "8.8.8.8, 208.67.222.222", profile.localPort + 63, "")
}
case _ => {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, "")
}
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-nat.conf"))(p => {
p.println(conf)
})
val cmd = getApplicationInfo.dataDir + "/pdnsd -c " + getApplicationInfo.dataDir + "/pdnsd-nat.conf"
val cmd = Array(getApplicationInfo.dataDir + "/pdnsd", "-c", getApplicationInfo.dataDir + "/pdnsd-nat.conf")
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
pdnsdProcess = new GuardedProcess(cmd.split(" ").toSeq).start()
pdnsdProcess = new GuardedProcess(cmd).start()
}
def startRedsocksDaemon() {
val conf = ConfigUtils.REDSOCKS.formatLocal(Locale.ENGLISH, profile.localPort)
val cmd = "%s/redsocks -c %s/redsocks-nat.conf"
.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, getApplicationInfo.dataDir)
val cmd = Array(getApplicationInfo.dataDir + "/redsocks", "-c", getApplicationInfo.dataDir + "/redsocks-nat.conf")
Utils.printToFile(new File(getApplicationInfo.dataDir + "/redsocks-nat.conf"))(p => {
p.println(conf)
})
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
redsocksProcess = new GuardedProcess(cmd.split(" ").toSeq).start()
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
redsocksProcess = new GuardedProcess(cmd).start()
}
/** Called when the activity is first created. */
def handleConnection {
def handleConnection() {
startDNSTunnel()
startRedsocksDaemon()
@@ -300,50 +314,32 @@ class ShadowsocksNatService extends BaseService {
su.addCommand((init_sb ++ http_sb).toArray)
}
override def startRunner(profile: Profile) = if (su == null) {
override def startRunner(profile: Profile) = if (su == null)
su = new Shell.Builder().useSU().setWantSTDERR(true).setWatchdogTimeout(10).open((_, exitCode, _) =>
if (exitCode == 0) super.startRunner(profile) else {
Log.wtf(TAG, "libsuperuser#55 has been fixed. Please remove the redundant code.")
su.close()
su = null
if (su != null) {
su.close()
su = null
}
super.stopRunner(true, getString(R.string.nat_no_root))
})
su.waitForIdle()
if (!su.isRunning) {
su.close()
su = null
super.stopRunner(true, getString(R.string.nat_no_root))
}
}
override def connect() {
super.connect()
if (profile != null) {
// Clean up
killProcesses()
// Clean up
killProcesses()
var resolved = false
if (!Utils.isNumeric(profile.host)) {
Utils.resolve(profile.host, enableIPv6 = true) match {
case Some(a) =>
profile.host = a
resolved = true
case None => resolved = false
}
} else {
resolved = true
}
if (!resolved) stopRunner(true, getString(R.string.invalid_server)) else {
handleConnection
// Set DNS
su.addCommand(Utils.FLUSH_DNS)
changeState(State.CONNECTED)
notification = new ShadowsocksNotification(this, profile.name, true)
}
if (!Utils.isNumeric(profile.host)) Utils.resolve(profile.host, enableIPv6 = true) match {
case Some(a) => profile.host = a
case None => throw NameNotResolvedException()
}
handleConnection()
// Set DNS
su.addCommand(Utils.FLUSH_DNS)
changeState(State.CONNECTED)
notification = new ShadowsocksNotification(this, profile.name, true)
}
override def stopRunner(stopService: Boolean, msg: String = null) {
@@ -19,7 +19,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
private val keyGuard = service.getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager]
private lazy val nm = service.getSystemService(Context.NOTIFICATION_SERVICE).asInstanceOf[NotificationManager]
private lazy val callback = new Stub {
override def stateChanged(state: Int, msg: String) = () // ignore
override def stateChanged(state: Int, profileName: String, msg: String) = () // ignore
override def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) {
val txr = TrafficMonitor.formatTraffic(txRate)
val rxr = TrafficMonitor.formatTraffic(rxRate)
@@ -49,7 +49,6 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
case _ =>
}
private lazy val style = new BigTextStyle(builder)
private val showOnUnlock = visible && Utils.isLollipopOrAbove
private var isVisible = true
update(if (service.getSystemService(Context.POWER_SERVICE).asInstanceOf[PowerManager].isScreenOn)
Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
@@ -57,19 +56,19 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
val screenFilter = new IntentFilter()
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
if (showOnUnlock) screenFilter.addAction(Intent.ACTION_USER_PRESENT)
if (visible && Utils.isLollipopOrAbove) screenFilter.addAction(Intent.ACTION_USER_PRESENT)
service.registerReceiver(lockReceiver, screenFilter)
private def update(action: String, forceShow: Boolean = false) =
if (forceShow || service.getState == State.CONNECTED) action match {
case Intent.ACTION_SCREEN_OFF =>
setVisible(false, forceShow)
setVisible(visible && !Utils.isLollipopOrAbove, forceShow)
unregisterCallback // unregister callback to save battery
case Intent.ACTION_SCREEN_ON =>
setVisible(showOnUnlock && !keyGuard.inKeyguardRestrictedInputMode, forceShow)
setVisible(visible && Utils.isLollipopOrAbove && !keyGuard.inKeyguardRestrictedInputMode, forceShow)
service.binder.registerCallback(callback)
callbackRegistered = true
case Intent.ACTION_USER_PRESENT => setVisible(showOnUnlock, forceShow)
case Intent.ACTION_USER_PRESENT => setVisible(true, forceShow)
}
private def unregisterCallback = if (callbackRegistered) {
@@ -68,7 +68,7 @@ class ShadowsocksQuickSwitchActivity extends AppCompatActivity {
profilesList.setItemAnimator(new DefaultItemAnimator)
profilesList.setAdapter(profilesAdapter)
if (app.profileId >= 0) lm.scrollToPosition(profilesAdapter.profiles.zipWithIndex.collectFirst {
case (profile, i) if profile.id == app.profileId => i + 1
case (profile, i) if profile.id == app.profileId => i
}.getOrElse(0))
}
}
@@ -5,14 +5,15 @@ import java.util.Locale
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.{Intent, SharedPreferences}
import android.net.Uri
import android.os.{Build, Bundle}
import android.os.Bundle
import android.preference.{Preference, PreferenceFragment, SwitchPreference}
import android.support.design.widget.Snackbar
import android.support.v7.app.AlertDialog
import android.webkit.{WebView, WebViewClient}
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.preferences._
import com.github.shadowsocks.utils.{Key, Utils}
import com.github.shadowsocks.utils.{Key, TcpFastOpen, Utils}
object ShadowsocksSettings {
// Constants
@@ -36,10 +37,8 @@ object ShadowsocksSettings {
}
def updateSummaryEditTextPreference(pref: Preference, value: String) {
if (value != null) {
pref.setSummary(value)
pref.asInstanceOf[SummaryEditTextPreference].setText(value)
}
pref.setSummary(value)
pref.asInstanceOf[SummaryEditTextPreference].setText(value)
}
def updateSwitchPreference(pref: Preference, value: Boolean) {
@@ -156,15 +155,27 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
}
switch.setChecked(BootReceiver.getEnabled(activity))
val tfo = findPreference(Key.tfo).asInstanceOf[SwitchPreference]
tfo.setChecked(TcpFastOpen.sendEnabled)
tfo.setOnPreferenceChangeListener((_, v) => {
val value = v.asInstanceOf[Boolean]
val result = TcpFastOpen.enabled(value)
if (result != null && result != "Success.")
Snackbar.make(activity.findViewById(android.R.id.content), result, Snackbar.LENGTH_LONG).show()
value == TcpFastOpen.sendEnabled
})
if (!TcpFastOpen.supported) {
tfo.setEnabled(false)
tfo.setSummary(getString(R.string.tcp_fastopen_summary_unsupported, java.lang.System.getProperty("os.version")))
}
findPreference("recovery").setOnPreferenceClickListener((preference: Preference) => {
app.track(TAG, "reset")
activity.recovery()
true
})
val flush = findPreference("flush_dnscache")
if (Build.VERSION.SDK_INT < 17) flush.setSummary(R.string.flush_dnscache_summary)
flush.setOnPreferenceClickListener(_ => {
findPreference("flush_dnscache").setOnPreferenceClickListener(_ => {
app.track(TAG, "flush_dnscache")
activity.flushDnsCache()
true
@@ -186,7 +197,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
})
new AlertDialog.Builder(activity)
.setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, app.getVersionName))
.setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, BuildConfig.VERSION_NAME))
.setNegativeButton(getString(android.R.string.ok), null)
.setView(web)
.create()
@@ -196,7 +207,19 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
}
def refreshProfile() {
profile = app.currentProfile.get
profile = app.currentProfile match {
case Some(p) => p
case None =>
app.profileManager.getFirstProfile match {
case Some(p) =>
app.profileId(p.id)
p
case None =>
val default = app.profileManager.createDefault()
app.profileId(default.id)
default
}
}
isProxyApps.setChecked(profile.proxyApps)
}
@@ -5,7 +5,6 @@ import android.graphics.drawable.Icon
import android.service.quicksettings.{Tile, TileService}
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.utils.{State, Utils}
import com.github.shadowsocks.ShadowsocksApplication.app
/**
* @author Mygod
@@ -23,30 +22,29 @@ final class ShadowsocksTileService extends TileService with ServiceBoundContext
private lazy val iconConnected = Icon.createWithResource(this, R.drawable.ic_start_connected)
private lazy val callback = new IShadowsocksServiceCallback.Stub {
def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) = ()
def stateChanged(state: Int, msg: String) {
def stateChanged(state: Int, profileName: String, msg: String) {
val tile = getQsTile
state match {
case State.STOPPED =>
tile.setIcon(iconIdle)
tile.setLabel(getString(R.string.app_name))
tile.setState(Tile.STATE_INACTIVE)
case State.CONNECTED =>
tile.setIcon(iconConnected)
tile.setLabel(app.currentProfile match {
case Some(profile) => profile.name
case None => getString(R.string.app_name)
})
tile.setState(Tile.STATE_ACTIVE)
case _ =>
tile.setIcon(iconBusy)
tile.setLabel(getString(R.string.app_name))
tile.setState(Tile.STATE_UNAVAILABLE)
if (tile != null) {
state match {
case State.STOPPED =>
tile.setIcon(iconIdle)
tile.setLabel(getString(R.string.app_name))
tile.setState(Tile.STATE_INACTIVE)
case State.CONNECTED =>
tile.setIcon(iconConnected)
tile.setLabel(if (profileName == null) getString(R.string.app_name) else profileName)
tile.setState(Tile.STATE_ACTIVE)
case _ =>
tile.setIcon(iconBusy)
tile.setLabel(getString(R.string.app_name))
tile.setState(Tile.STATE_UNAVAILABLE)
}
tile.updateTile
}
tile.updateTile
}
}
override def onServiceConnected() = callback.stateChanged(bgService.getState, null)
override def onServiceConnected() = callback.stateChanged(bgService.getState, bgService.getProfileName, null)
override def onCreate {
super.onCreate
@@ -40,17 +40,17 @@
package com.github.shadowsocks
import java.io.File
import java.lang.Process
import java.util.Locale
import android.annotation.SuppressLint
import android.content._
import android.content.pm.PackageManager.NameNotFoundException
import android.net.VpnService
import android.os._
import android.util.Log
import com.github.shadowsocks.utils._
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils._
import scala.collection.mutable.ArrayBuffer
@@ -63,11 +63,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
var vpnThread: ShadowsocksVpnThread = _
private var notification: ShadowsocksNotification = _
var kcptunProcess: Process = _
var sslocalProcess: Process = _
var sstunnelProcess: Process = _
var pdnsdProcess: Process = _
var tun2socksProcess: Process = _
var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _
var tun2socksProcess: GuardedProcess = _
override def onBind(intent: Intent): IBinder = {
val action = intent.getAction
@@ -149,73 +148,60 @@ class ShadowsocksVpnService extends VpnService with BaseService {
override def connect() = {
super.connect()
if (profile != null) {
vpnThread = new ShadowsocksVpnThread(this)
vpnThread.start()
vpnThread = new ShadowsocksVpnThread(this)
vpnThread.start()
// reset the context
killProcesses()
// reset the context
killProcesses()
// Resolve the server address
var resolved: Boolean = false
if (!Utils.isNumeric(profile.host)) {
Utils.resolve(profile.host, enableIPv6 = true) match {
case Some(addr) =>
profile.host = addr
resolved = true
case None => resolved = false
}
} else {
resolved = true
}
if (!resolved) stopRunner(true, getString(R.string.invalid_server)) else if (handleConnection) {
changeState(State.CONNECTED)
notification = new ShadowsocksNotification(this, profile.name)
} else {
changeState(State.STOPPED, getString(R.string.service_failed))
stopRunner(true)
}
// Resolve the server address
if (!Utils.isNumeric(profile.host)) Utils.resolve(profile.host, enableIPv6 = true) match {
case Some(addr) => profile.host = addr
case None => throw NameNotResolvedException()
}
handleConnection()
changeState(State.CONNECTED)
notification = new ShadowsocksNotification(this, profile.name)
}
/** Called when the activity is first created. */
def handleConnection: Boolean = {
def handleConnection() {
if (!profile.udpdns) {
startDnsDaemon()
startDnsTunnel()
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
if (profile.kcp) {
startKcptunDaemon()
}
startShadowsocksDaemon()
if (profile.udpdns) {
if (profile.udpdns && profile.kcp) {
startShadowsocksUDPDaemon()
}
val fd = startVpn()
val ret = sendFd(fd)
if (ret && profile.kcp) {
startKcptunDaemon()
if (!profile.udpdns) {
startDnsDaemon()
startDnsTunnel()
}
ret
}
def startKcptunDaemon() {
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/kcptun"
if (profile.kcpcli == null) profile.kcpcli = ""
val cmd = ArrayBuffer(getApplicationInfo.dataDir + "/kcptun"
, "-r", profile.host + ":" + profile.kcpPort
, "-l", "127.0.0.1:" + (profile.localPort + 90)
, "--path", getApplicationInfo.dataDir + "/protect_path"
, profile.kcpcli)
, "--path", getApplicationInfo.dataDir + "/protect_path")
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
case exc: Exception => throw KcpcliParseException(exc)
}
if (BuildConfig.DEBUG)
Log.d(TAG, cmd.mkString(" "))
kcptunProcess = new GuardedProcess(cmd.mkString(" ").split(" ").toSeq).start()
kcptunProcess = new GuardedProcess(cmd).start()
}
def startShadowsocksUDPDaemon() {
@@ -226,8 +212,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-local", "-V", "-U"
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/ss-local", "-V", "-U"
, "-b", "127.0.0.1"
, "-t", "600"
, "-P", getApplicationInfo.dataDir
@@ -241,15 +226,17 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
def startShadowsocksDaemon() {
if (profile.route != Route.ALL) {
val acl: Array[Array[String]] = profile.route match {
case Route.BYPASS_LAN => Array(getResources.getStringArray(R.array.private_route))
case Route.BYPASS_CHN => Array(getResources.getStringArray(R.array.chn_route))
if (profile.route != Route.ALL && profile.route != Route.GFWLIST) {
val acl: Array[String] = profile.route match {
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route)
case Route.BYPASS_LAN_CHN =>
Array(getResources.getStringArray(R.array.private_route), getResources.getStringArray(R.array.chn_route))
getResources.getStringArray(R.array.private_route) ++ getResources.getStringArray(R.array.chn_route)
case Route.CHINALIST =>
Array("[bypass_all]", "[white_list]") ++ getResources.getStringArray(R.array.chn_route)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => {
acl.flatten.foreach(p.println)
acl.foreach(p.println)
})
}
@@ -266,8 +253,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-local", "-V"
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/ss-local", "-V"
, "-b", "127.0.0.1"
, "-t", "600"
, "-P", getApplicationInfo.dataDir
@@ -275,11 +261,18 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.auth) cmd += "-A"
if (profile.udpdns && !profile.kcp) cmd += "-u"
if (profile.route != Route.ALL) {
cmd += "--acl"
cmd += (getApplicationInfo.dataDir + "/acl.list")
if (profile.route == Route.GFWLIST)
cmd += (getApplicationInfo.dataDir + "/gfwlist.acl")
else
cmd += (getApplicationInfo.dataDir + "/acl.list")
}
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
sslocalProcess = new GuardedProcess(cmd).start()
@@ -298,8 +291,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-tunnel"
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/ss-tunnel"
, "-V"
, "-t", "10"
, "-b", "127.0.0.1"
@@ -316,11 +308,20 @@ class ShadowsocksVpnService extends VpnService with BaseService {
def startDnsDaemon() {
val ipv6 = if (profile.ipv6) "" else "reject = ::/0;"
val conf = {
if (profile.route == Route.BYPASS_CHN || profile.route == Route.BYPASS_LAN_CHN) {
val conf = profile.route match {
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN => {
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, getBlackList, profile.localPort + 63, ipv6)
} else {
}
case Route.GFWLIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, "1.2.4.8, 208.67.222.222", profile.localPort + 63, "")
}
case Route.CHINALIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, "8.8.8.8, 208.67.222.222", profile.localPort + 63, ipv6)
}
case _ => {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, profile.localPort + 63, ipv6)
}
@@ -328,13 +329,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => {
p.println(conf)
})
val cmd = getApplicationInfo.dataDir + "/pdnsd -c " + getApplicationInfo.dataDir + "/pdnsd-vpn.conf"
val cmd = Array(getApplicationInfo.dataDir + "/pdnsd", "-c", getApplicationInfo.dataDir + "/pdnsd-vpn.conf")
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
pdnsdProcess = new GuardedProcess(cmd.split(" ").toSeq).start()
pdnsdProcess = new GuardedProcess(cmd).start()
}
@SuppressLint(Array("NewApi"))
def startVpn(): Int = {
val builder = new Builder()
@@ -342,7 +344,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
.setSession(profile.name)
.setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
.addDnsServer("8.8.8.8")
builder.addDnsServer("8.8.8.8")
if (profile.ipv6) {
builder.addAddress(PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "1"), 126)
@@ -361,7 +364,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
} catch {
case ex: NameNotFoundException =>
Log.e(TAG, "Invalid package name", ex);
Log.e(TAG, "Invalid package name", ex)
}
}
}
@@ -383,45 +386,36 @@ class ShadowsocksVpnService extends VpnService with BaseService {
conn = builder.establish()
if (conn == null) changeState(State.STOPPED, getString(R.string.reboot_required))
} catch {
case ex: IllegalStateException =>
changeState(State.STOPPED, ex.getMessage)
conn = null
case ex: Exception =>
ex.printStackTrace()
conn = null
}
if (conn == null) {
stopRunner(true)
return -1
app.track(ex)
stopRunner(true, ex.getMessage)
return -1
}
val fd = conn.getFd
var cmd = (getApplicationInfo.dataDir +
"/tun2socks --netif-ipaddr %s "
+ "--netif-netmask 255.255.255.0 "
+ "--socks-server-addr 127.0.0.1:%d "
+ "--tunfd %d "
+ "--tunmtu %d "
+ "--sock-path %s "
+ "--loglevel 3")
.formatLocal(Locale.ENGLISH,
PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"),
profile.localPort, fd, VPN_MTU, getApplicationInfo.dataDir + "/sock_path")
var cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/tun2socks",
"--netif-ipaddr", PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"),
"--netif-netmask", "255.255.255.0",
"--socks-server-addr", "127.0.0.1:" + profile.localPort,
"--tunfd", fd.toString,
"--tunmtu", VPN_MTU.toString,
"--sock-path", getApplicationInfo.dataDir + "/sock_path",
"--loglevel", "3")
if (profile.ipv6)
cmd += " --netif-ip6addr " + PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "2")
cmd += ("--netif-ip6addr", PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "2"))
if (profile.udpdns)
cmd += " --enable-udprelay"
cmd += "--enable-udprelay"
else
cmd += " --dnsgw %s:%d".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"),
profile.localPort + 53)
cmd += ("--dnsgw", "%s:%d".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"),
profile.localPort + 53))
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
tun2socksProcess = new GuardedProcess(cmd.split(" ").toSeq).start(() => sendFd(fd))
tun2socksProcess = new GuardedProcess(cmd).start(() => sendFd(fd))
fd
}
@@ -44,6 +44,7 @@ import java.util.concurrent.Executors
import android.net.{LocalServerSocket, LocalSocket, LocalSocketAddress}
import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app
object ShadowsocksVpnThread {
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
@@ -56,7 +57,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
lazy val PATH = vpnService.getApplicationInfo.dataDir + "/protect_path"
@volatile var isRunning: Boolean = true
@volatile var serverSocket: LocalServerSocket = null
@volatile var serverSocket: LocalServerSocket = _
def closeServerSocket() {
if (serverSocket != null) {
@@ -76,11 +77,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
override def run() {
try {
new File(PATH).delete()
} catch {
case _: Exception => // ignore
}
new File(PATH).delete()
try {
val localSocket = new LocalSocket
@@ -89,6 +86,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
} catch {
case e: IOException =>
Log.e(TAG, "unable to bind", e)
app.track(e)
return
}
@@ -127,6 +125,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
} catch {
case e: Exception =>
Log.e(TAG, "Error when protect socket", e)
app.track(e)
}
// close socket
@@ -140,6 +139,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
} catch {
case e: IOException =>
Log.e(TAG, "Error when accept socket", e)
app.track(e)
return
}
}
@@ -64,7 +64,7 @@ object DBHelper {
}
class DBHelper(val context: Context)
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 18) {
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 20) {
import DBHelper._
lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
@@ -83,10 +83,11 @@ class DBHelper(val context: Context)
}
if (oldVersion < 8) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 9) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR DEFAULT 'all';")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;")
}
if (oldVersion < 10) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
@@ -128,11 +129,17 @@ class DBHelper(val context: Context)
if (oldVersion < 17) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcp SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpcli VARCHAR;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpcli VARCHAR DEFAULT " +
"'--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0';")
} else if (oldVersion < 20) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpcli = '--crypt none --mode normal --mtu 1200 --nocomp " +
"--dscp 46 --parityshard 0' WHERE kcpcli IS NULL;")
}
if (oldVersion < 18) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpPort INTEGER;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpPort INTEGER DEFAULT 8399;")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpPort = 8399 WHERE kcpPort = 0;")
}
}
}
@@ -106,7 +106,7 @@ class Profile {
var kcpPort: Int = 8399
@DatabaseField
var kcpcli: String = ""
var kcpcli: String = "--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0"
override def toString = "ss://" + Base64.encodeToString("%s%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
method, if (auth) "-auth" else "", password, host, remotePort).getBytes, Base64.NO_PADDING | Base64.NO_WRAP)
@@ -53,9 +53,9 @@ class ProfileManager(dbHelper: DBHelper) {
def setProfileAddedListener(listener: Profile => Any) = this.profileAddedListener = listener
def createProfile(p: Profile = null): Profile = {
val profile = if (p == null) new Profile else p
profile.id = 0
try {
val profile = if (p == null) new Profile else p
profile.id = 0
app.currentProfile match {
case Some(oldProfile) =>
// Copy Feature Settings from old profile
@@ -72,12 +72,12 @@ class ProfileManager(dbHelper: DBHelper) {
if (last != null && last.length == 1 && last(0) != null) profile.userOrder = last(0).toInt + 1
dbHelper.profileDao.createOrUpdate(profile)
if (profileAddedListener != null) profileAddedListener(profile)
profile
} catch {
case ex: Exception =>
Log.e(TAG, "addProfile", ex)
p
app.track(ex)
}
profile
}
def updateProfile(profile: Profile): Boolean = {
@@ -87,6 +87,7 @@ class ProfileManager(dbHelper: DBHelper) {
} catch {
case ex: Exception =>
Log.e(TAG, "updateProfile", ex)
app.track(ex)
false
}
}
@@ -100,6 +101,7 @@ class ProfileManager(dbHelper: DBHelper) {
} catch {
case ex: Exception =>
Log.e(TAG, "getProfile", ex)
app.track(ex)
None
}
}
@@ -111,6 +113,7 @@ class ProfileManager(dbHelper: DBHelper) {
} catch {
case ex: Exception =>
Log.e(TAG, "delProfile", ex)
app.track(ex)
false
}
}
@@ -122,6 +125,7 @@ class ProfileManager(dbHelper: DBHelper) {
} catch {
case ex: Exception =>
Log.e(TAG, "getAllProfiles", ex)
app.track(ex)
None
}
}
@@ -133,6 +137,7 @@ class ProfileManager(dbHelper: DBHelper) {
} catch {
case ex: Exception =>
Log.e(TAG, "getAllProfiles", ex)
app.track(ex)
None
}
}
@@ -0,0 +1,33 @@
package com.github.shadowsocks.preferences
import android.app.AlertDialog
import android.content.{Context, DialogInterface}
import android.util.AttributeSet
import eu.chainfire.libsuperuser.Shell
import scala.collection.JavaConversions._
/**
* @author Mygod
*/
class KcpCliPreference(context: Context, attrs: AttributeSet) extends SummaryEditTextPreference(context, attrs) {
override def onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setNeutralButton("?", ((_, _) => new AlertDialog.Builder(context)
.setTitle("?")
.setMessage(asScalaBuffer(Shell.SH.run(context.getApplicationInfo.dataDir + "/kcptun --help"))
.dropWhile(line => line != "GLOBAL OPTIONS:")
.drop(1)
.takeWhile(line => line.length() > 3)
.filter(line =>
!line.startsWith(" --localaddr ") &&
!line.startsWith(" --remoteaddr ") &&
!line.startsWith(" --path ") &&
!line.startsWith(" --help,") &&
!line.startsWith(" --version,"))
.mkString("\n")
.replaceAll(" {2,}", "\n")
.substring(1)) // remove 1st \n
.show()): DialogInterface.OnClickListener)
}
}
@@ -41,6 +41,7 @@ package com.github.shadowsocks.preferences
import android.content.Context
import android.preference.EditTextPreference
import android.text.TextUtils
import android.util.AttributeSet
class SummaryEditTextPreference(context: Context, attrs: AttributeSet, defStyle: Int)
@@ -57,7 +58,7 @@ class SummaryEditTextPreference(context: Context, attrs: AttributeSet, defStyle:
}
override def setSummary(summary: CharSequence) {
if (summary.toString.isEmpty) {
if (TextUtils.isEmpty(summary)) {
super.setSummary(mDefaultSummary)
} else {
super.setSummary(summary)
@@ -4,14 +4,11 @@ package com.github.shadowsocks.utils
* @author Mygod
*/
object CloseUtils {
type Closeable = {
def close()
}
type Disconnectable = {
def disconnect()
}
def autoClose[A <: Closeable, B](x: => A)(block: A => B): B = {
def autoClose[A <: AutoCloseable, B](x: => A)(block: A => B): B = {
var a: Option[A] = None
try {
a = Some(x)
@@ -116,7 +116,7 @@ object ConfigUtils {
|
|server {
| label = "china-servers";
| ip = 114.114.114.114, 112.124.47.27;
| ip = 1.2.4.8, 208.67.222.222, 114.114.114.114;
| timeout = 4;
| exclude = %s;
| policy = included;
@@ -141,6 +141,47 @@ object ConfigUtils {
| soa=localhost,root.localhost,42,86400,900,86400,86400;
|}
""".stripMargin
val PDNSD_UDP =
"""
|global {
| perm_cache = 2048;
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = udp_tcp;
| run_ipv4 = on;
| min_ttl = 15m;
| max_ttl = 1w;
| timeout = 10;
| daemon = off;
|}
|
|server {
| label = "remote-servers";
| ip = %s;
| timeout = 5;
| uptest = none;
| preset = on;
|}
|
|server {
| label = "local-server";
| ip = 127.0.0.1;
| port = %d;
| %s
| reject_policy = negate;
| reject_recursively = on;
|}
|
|rr {
| name=localhost;
| reverse=on;
| a=127.0.0.1;
| owner=localhost;
| soa=localhost,root.localhost,42,86400,900,86400,86400;
|}
""".stripMargin
}
object Key {
@@ -171,6 +212,9 @@ object Key {
val kcp = "kcp"
val kcpPort = "kcpPort"
val kcpcli = "kcpcli"
val tfo = "tcp_fastopen"
val currentVersionCode = "currentVersionCode"
}
object State {
@@ -192,4 +236,6 @@ object Route {
val BYPASS_LAN = "bypass-lan"
val BYPASS_CHN = "bypass-china"
val BYPASS_LAN_CHN = "bypass-lan-china"
val GFWLIST = "gfwlist"
val CHINALIST = "china-list"
}
@@ -0,0 +1,40 @@
package com.github.shadowsocks.utils
import java.io.File
import eu.chainfire.libsuperuser.Shell
import scala.collection.JavaConverters._
/**
* @author Mygod
*/
object TcpFastOpen {
/**
* Is kernel version >= 3.7.1.
*/
lazy val supported = "^(\\d+)\\.(\\d+)\\.(\\d+)".r.findFirstMatchIn(System.getProperty("os.version")) match {
case Some(m) =>
val kernel = m.group(1).toInt
if (kernel < 3) false else if (kernel > 3) true else {
val major = m.group(2).toInt
if (major < 7) false else if (major > 7) true else m.group(3).toInt >= 1
}
case _ => false
}
def sendEnabled = {
val file = new File("/proc/sys/net/ipv4/tcp_fastopen")
file.canRead && (Utils.readAllLines(file).toInt & 1) > 0
}
def enabled(value: Boolean): String = if (sendEnabled != value) {
val res = Shell.run("su", Array(
"if echo " + (if (value) 3 else 0) + " > /proc/sys/net/ipv4/tcp_fastopen; then",
" echo Success.",
"else",
" echo Failed.",
"fi"), null, true)
if (res != null) res.asScala.mkString("\n") else null
} else null
}
@@ -46,13 +46,14 @@ import java.util.concurrent.Executors
import android.content.Context
import android.net.{LocalServerSocket, LocalSocket, LocalSocketAddress}
import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app
class TrafficMonitorThread(context: Context) extends Thread {
val TAG = "TrafficMonitorThread"
lazy val PATH = context.getApplicationInfo.dataDir + "/stat_path"
@volatile var serverSocket: LocalServerSocket = null
@volatile var serverSocket: LocalServerSocket = _
@volatile var isRunning: Boolean = true
def closeServerSocket() {
@@ -73,11 +74,7 @@ class TrafficMonitorThread(context: Context) extends Thread {
override def run() {
try {
new File(PATH).delete()
} catch {
case _: Exception => // ignore
}
new File(PATH).delete()
try {
val localSocket = new LocalSocket
@@ -113,6 +110,7 @@ class TrafficMonitorThread(context: Context) extends Thread {
} catch {
case e: Exception =>
Log.e(TAG, "Error when recv traffic stat", e)
app.track(e)
}
// close socket
@@ -126,6 +124,7 @@ class TrafficMonitorThread(context: Context) extends Thread {
} catch {
case e: IOException =>
Log.e(TAG, "Error when accept socket", e)
app.track(e)
return
}
}
@@ -38,15 +38,16 @@
*/
package com.github.shadowsocks.utils
import java.io.File
import java.net._
import java.security.MessageDigest
import java.util.{Scanner, StringTokenizer}
import android.animation.{Animator, AnimatorListenerAdapter}
import android.content.pm.PackageManager
import android.content.{Context, Intent}
import android.graphics._
import android.os.Build
import android.provider.Settings
import android.util.{Base64, DisplayMetrics, Log}
import android.view.View.MeasureSpec
import android.view.{Gravity, View, Window}
@@ -56,6 +57,8 @@ import com.github.shadowsocks.{BuildConfig, ShadowsocksRunnerService}
import eu.chainfire.libsuperuser.Shell
import org.xbill.DNS._
import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Try}
@@ -109,6 +112,13 @@ object Utils {
})
}
def readAllLines(f: File) = {
val scanner = new Scanner(f)
try {
scanner.useDelimiter("\\Z")
scanner.next()
} finally scanner.close()
}
def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) {
val p = new java.io.PrintWriter(f)
try {
@@ -118,6 +128,48 @@ object Utils {
}
}
/**
* Crack a command line.
* Based on: https://github.com/apache/ant/blob/588ce1f/src/main/org/apache/tools/ant/types/Commandline.java#L471
* @param toProcess the command line to process.
* @return the command line broken into strings.
* An empty or null toProcess parameter results in a zero sized ArrayBuffer.
*/
def translateCommandline(toProcess: String): ArrayBuffer[String] = {
if (toProcess == null || toProcess.length == 0) return ArrayBuffer[String]()
val tok = new StringTokenizer(toProcess, "\"' ", true)
val result = ArrayBuffer[String]()
val current = new StringBuilder()
var quote = ' '
var last = " "
while (tok.hasMoreTokens) {
val nextTok = tok.nextToken
quote match {
case '\'' => nextTok match {
case "'" => quote = ' '
case _ => current.append(nextTok)
}
case '"' => nextTok match {
case "\"" => quote = ' '
case _ => current.append(nextTok)
}
case _ => nextTok match {
case "'" => quote = '\''
case "\"" => quote = '"'
case " " => if (last != " ") {
result.append(current.toString)
current.setLength(0)
}
case _ => current.append(nextTok)
}
}
last = nextTok
}
if (current.nonEmpty) result.append(current.toString)
if (quote == '\'' || quote == '"') throw new Exception("Unbalanced quotes in " + toProcess)
result
}
// Because /sys/class/net/* isn't accessible since API level 24
final val FLUSH_DNS = "for if in /sys/class/net/*; do " +
"if [ \"down\" != $(cat $if/operstate) ]; then " + // up or unknown
@@ -128,25 +180,7 @@ object Utils {
// Blocked > 3 seconds
def toggleAirplaneMode(context: Context) = {
val result = Shell.SU.run(FLUSH_DNS)
if (result != null && !result.isEmpty) true else if (Build.VERSION.SDK_INT < 17) {
toggleBelowApiLevel17(context)
true
} else false
}
//noinspection ScalaDeprecation
private def toggleBelowApiLevel17(context: Context) {
// Android 4.2 below
Settings.System.putInt(context.getContentResolver, Settings.System.AIRPLANE_MODE_ON, 1)
val enableIntent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
enableIntent.putExtra("state", true)
context.sendBroadcast(enableIntent)
Thread.sleep(3000)
Settings.System.putInt(context.getContentResolver, Settings.System.AIRPLANE_MODE_ON, 0)
val disableIntent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
disableIntent.putExtra("state", false)
context.sendBroadcast(disableIntent)
result != null && !result.isEmpty
}
def resolve(host: String, addrType: Int): Option[String] = {
@@ -210,29 +244,22 @@ object Utils {
*/
def isIPv6Support: Boolean = {
try {
val interfaces = NetworkInterface.getNetworkInterfaces
while (interfaces.hasMoreElements) {
val intf = interfaces.nextElement()
val addrs = intf.getInetAddresses
while (addrs.hasMoreElements) {
val addr = addrs.nextElement()
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
if (addr.isInstanceOf[Inet6Address]) {
if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected")
return true
}
for (intf <- enumerationAsScalaIterator(NetworkInterface.getNetworkInterfaces))
for (addr <- enumerationAsScalaIterator(intf.getInetAddresses))
if (addr.isInstanceOf[Inet6Address] && !addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected")
return true
}
}
}
} catch {
case ex: Exception =>
Log.e(TAG, "Failed to get interfaces' addresses.", ex)
app.track(ex)
}
false
}
def startSsService(context: Context) {
val isInstalled: Boolean = app.settings.getBoolean(app.getVersionName, false)
val isInstalled: Boolean = app.settings.getBoolean(BuildConfig.VERSION_NAME, false)
if (!isInstalled) return
val intent = new Intent(context, classOf[ShadowsocksRunnerService])
@@ -245,7 +272,9 @@ object Utils {
}
private val handleFailure: Try[_] => Unit = {
case Failure(e) => e.printStackTrace()
case Failure(e) =>
e.printStackTrace()
app.track(e)
case _ =>
}
-25
View File
@@ -1,25 +0,0 @@
#!/bin/bash
export ARCH=`uname -m`
export ANDROID_NDK_HOME=$HOME/.android/android-ndk-r12b
export ANDROID_HOME=$HOME/.android/android-sdk-linux
export PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
if [ ! -d "$ANDROID_HOME" ]; then
mkdir -p $ANDROID_HOME
pushd $HOME/.android
wget -q http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
tar xf android-sdk_r24.4.1-linux.tgz
popd
fi
if [ ! -d "$ANDROID_NDK_HOME" ]; then
mkdir -p $ANDROID_NDK_HOME
pushd $HOME/.android
wget -q http://dl.google.com/android/repository/android-ndk-r12b-linux-${ARCH}.zip
unzip -q android-ndk-r12b-linux-${ARCH}.zip
popd
fi
( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk --filter tools,platform-tools,build-tools-24.0.0,android-23,android-24,extra-google-m2repository --no-ui -a
( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk --filter extra-android-m2repository --no-ui -a
BIN
View File
Binary file not shown.