Compare commits

...
136 Commits
Author SHA1 Message Date
Max Lv 59f02cfa53 Refine Firebase remote config 2017-04-28 16:50:41 +08:00
Max Lv bd782dae98 Avoid too many reschedules 2017-04-28 08:04:40 +08:00
Mygod ea738d17d7 Refine #1217 2017-04-24 11:51:47 +08:00
Mygod 915b1a74b1 Refine Firebase code 2017-04-24 11:37:47 +08:00
Mygod 9898ab88e0 Optimize firebase dependencies 2017-04-24 11:25:11 +08:00
Mygod c082a23c37 Update dependencies 2017-04-24 10:59:44 +08:00
Max Lv 6636cf815d Replace tag manager with firebase config 2017-04-23 23:11:44 +08:00
Max Lv 6df8f14fb2 Refine server name resolving 2017-04-23 21:49:01 +08:00
Max Lv 0b88881fa4 Update shadowsocks-libev 2017-04-20 14:10:09 +08:00
V.E.O 85532a6b48 Add multiple remote dns support and remove alternative dns in global mode (#1217)
* Add multiple remote dns support. Remove alternative dns in global mode.
* Remove a redundant comment
2017-04-14 23:32:14 -05:00
Mygod 93274ffb04 Update android-job 2017-04-14 00:08:17 +08:00
Max Lv fcd6ed847a Bump version 2017-04-12 09:42:45 +08:00
Xingyu C 36c4d80ee4 MaxPermSize was removed in JDK 1.8 (#1207) 2017-04-12 09:33:48 +08:00
Mygod e864873ee4 Update issue_template.md to include remote DNS 2017-04-10 00:26:40 +08:00
Max Lv 80411c61d6 Add build files for overture 2017-04-09 10:10:04 +08:00
Max Lv adb7285d54 Remove overture-android submodule 2017-04-09 09:41:37 +08:00
Max Lv 7356eded16 Fix #1197 2017-04-09 09:40:19 +08:00
Mygod 1268376b68 Fix hostname not displayed correctly in notification 2017-04-07 19:47:06 +08:00
Mygod b71019149f Update issue template 2017-04-07 19:35:37 +08:00
Mygod 451e9c1fd3 Update dependencies 2017-04-07 19:33:09 +08:00
Max Lv 9d5c9456c9 Update overture dependencies 2017-04-07 11:12:17 +08:00
Max Lv 4988faf574 Fix #1196 2017-04-07 10:14:03 +08:00
Mygod 189058b5c4 Fix a typo caused by copy and paste 2017-04-06 20:37:32 +08:00
Mygod 529ee3a929 Remove unused misc_cat 2017-03-30 22:03:16 +08:00
Mygod 90cc3dbf2d Update dependencies 2017-03-30 21:54:15 +08:00
Mygod 6fe38f76be Update dependencies 2017-03-25 00:11:54 +08:00
Mygod 4df1d68501 Handle crashes caused by incorrect configured scanners
Fixes #1170.
2017-03-20 13:39:49 +08:00
Mygod 9eceefcceb Fix building 2017-03-17 19:05:32 +08:00
Max Lv af6e58ec46 Fix #1166 2017-03-17 10:34:13 +08:00
Mygod 64c8529705 Update libsodium 2017-03-15 19:23:10 +08:00
Mygod b1670679d7 Add Korean 2017-03-15 18:55:03 +08:00
Mygod 5ac1343b5d Update strings.xml (POEditor.com) 2017-03-13 01:02:14 -05:00
Mygod 41f4f3a3bc Import strings.xml from POEditor.com
Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)
2017-03-13 13:58:51 +08:00
Max Lv c21bd74302 Merge branch 'beta' 2017-03-11 18:48:12 +08:00
Max Lv a8822330a3 Bump version 2017-03-11 18:43:03 +08:00
Max Lv 1cf6e4802c Update strings 2017-03-11 18:15:34 +08:00
Max Lv f64014eb7b Always forwording UDP traffic #1156 2017-03-11 15:49:06 +08:00
Mygod 2f6f991e6e Fix literal IPv6 address handling in URIs 2017-03-11 12:33:27 +08:00
Mygod 90e00a3857 Refine README.md 2017-03-11 12:11:52 +08:00
Mygod 712b764a9b Update README.md to reflect changes in sbt-android 2017-03-10 08:55:49 +08:00
Mygod 019f9d30e8 Remove redundant group in legacyPattern 2017-03-10 08:54:03 +08:00
Mygod 9c97dc9bf6 Update README.md to reflect changes in sbt-android 2017-03-09 23:54:48 +08:00
Mygod 7d2e0a515f Remove redundant group in legacyPattern 2017-03-09 20:46:34 +08:00
Mygod 26b68876f4 Update build instructions 2017-03-08 01:03:46 +08:00
Mygod a494f56ce8 Refine #1148 2017-03-08 00:25:05 +08:00
Max Lv a433822d0a Add arm64 support 2017-03-07 10:30:29 +08:00
Max Lv 60d7d90565 Fix a DNS issue with GFWList mode 2017-03-05 16:11:24 +08:00
Max Lv d2ba9493c0 Bump version 2017-03-04 10:26:43 +08:00
Max Lv 0eaece7af9 Remove external IP #1148 2017-03-04 09:56:52 +08:00
Max Lv 96320896aa Update licenses 2017-03-02 14:53:53 +08:00
Max Lv 54efe36ffc Add missing -V option 2017-03-02 09:28:12 +08:00
Max Lv d5d6740a5a Fix typos 2017-03-02 09:22:40 +08:00
Mygod 2e69eafdf8 Refine timeout for getting external IP 2017-03-01 22:49:10 +08:00
Mygod 86773085c3 Wait for process to terminate with a better way
Refine #1122.
2017-03-01 22:28:35 +08:00
Mygod b6667b9e0f Use more readable variable name 2017-03-01 17:25:23 +08:00
Max Lv 0ee7e9d40e Build overture from source code 2017-03-01 16:39:04 +08:00
Mygod 5f2ce1804f Refine Overture configuration generation
* Respect remoteDns settings more;
* Make code more readable;
* Handle illegal characters in remoteDns.
2017-03-01 16:08:19 +08:00
Mygod a5e316999a Update dependencies 2017-03-01 15:30:03 +08:00
Max Lv 64a3d75ba2 Update shadowsocks-libev 2017-03-01 12:02:36 +08:00
Max Lv 9d09c9a097 Bump version 2017-03-01 09:59:00 +08:00
Max Lv b78f48504f Merge branch 'develop' into beta 2017-03-01 09:57:45 +08:00
Max Lv a61b03c86c Add 600ms timeout for waitpid 2017-03-01 09:53:35 +08:00
Max Lv f43362fb18 Fix overture with NAT mode 2017-02-25 16:58:31 +08:00
Max Lv 4f0e527df7 Replace PDNSD with overture 2017-02-24 21:00:40 +08:00
Max Lv a8878298cc Bump version 2017-02-24 19:44:46 +08:00
Max Lv c920c0daa4 Bump version 2017-02-24 19:37:23 +08:00
Mygod 8e3854b496 Update dependencies 2017-02-24 19:37:23 +08:00
Max Lv ad42167b2e Update shadowsocks-libev 2017-02-24 19:37:23 +08:00
Max Lv 3bf0963c5a Trim remoteDns 2017-02-24 19:37:23 +08:00
Max Lv c3315d35ed Fix #1130 2017-02-24 19:37:23 +08:00
Max Lv 103dc10a0a Update shadowsocks-libev 2017-02-24 19:37:23 +08:00
Max Lv 36c94b8d83 Fix a memory leak 2017-02-24 19:37:22 +08:00
Max Lv 890be54403 Bump version 2017-02-24 19:36:44 +08:00
Max Lv 31984d1001 Add prebuilt overture libraries 2017-02-24 17:51:06 +08:00
Mygod df72b29302 Fix malformed URL in about.html 2017-02-23 15:17:57 +08:00
Mygod f30c31ae3e Bump plugin lib version 2017-02-23 14:56:23 +08:00
Mygod 05963b6c82 Update support library 2017-02-23 14:55:32 +08:00
Mygod 98da4cd6e5 Update dependencies 2017-02-23 14:55:07 +08:00
Max Lv 64da35c59b Update shadowsocks-libev 2017-02-22 14:56:31 +08:00
Max Lv 458a3473b1 Trim remoteDns 2017-02-17 20:13:13 +08:00
Max Lv 66db347215 Fix #1130 2017-02-17 19:14:33 +08:00
Max Lv f9e32c995b Update shadowsocks-libev 2017-02-17 13:36:41 +08:00
Max Lv 0626ce8787 Fix a memory leak 2017-02-17 12:35:15 +08:00
Max Lv 11d827c6ba Bump version 2017-02-13 09:29:11 +08:00
Max Lv dc46cd971a Merge branch 'beta' 2017-02-13 09:21:18 +08:00
Max Lv 876e92f275 Bump version 2017-02-13 09:17:59 +08:00
Max Lv 41747c976b Update shadowsocks-libev 2017-02-12 20:24:56 +08:00
Max Lv 8277b07d24 Bump version 2017-02-10 10:16:27 +08:00
Max Lv b982867130 Merge branch 'beta' 2017-02-10 10:05:18 +08:00
Max Lv 491710679a Fix #1111 2017-02-09 10:50:44 +08:00
Max Lv 24c6aca85c Add remote DNS to route 2017-02-09 10:02:02 +08:00
Mygod 293e9b5dff Fix equals missing in Subnet 2017-02-08 18:47:53 +08:00
Mygod 3ac77e1f8d Fix crash on back pressed after MainActivity recreated 2017-02-08 18:47:23 +08:00
Mygod fd2c67d0b8 Add missing file 2017-02-08 17:19:22 +08:00
Mygod 684698c8e4 Refine URLs 2017-02-08 17:18:04 +08:00
Mygod 6a02d4b985 Add change log to plugin library 2017-02-08 17:13:17 +08:00
Mygod 7219ce429c Bump version 2017-02-08 17:07:56 +08:00
Max Lv 9975b135d0 Remove chacha20-poly1305 2017-02-08 12:19:51 +08:00
Max Lv 5d46ef45e3 Bump version 2017-02-08 12:03:16 +08:00
Max Lv 018aa781d3 Merge branch 'develop' into beta 2017-02-08 12:01:05 +08:00
Max Lv f7e2f8694d Add missing rules of base64.c 2017-02-08 11:58:15 +08:00
Max Lv 2d86122297 Update shadowsocks-libev 2017-02-08 11:58:15 +08:00
Max Lv ad0578d073 Fix UDP relay issues 2017-02-08 11:58:15 +08:00
Max Lv b671bc2c50 Update shadowsocks-libev 2017-02-08 11:58:15 +08:00
Mygod be5036183d Fix issues about SIP002 2017-02-08 11:58:15 +08:00
Mygod 212c3f666b Update shadowsocks-libev 2017-02-08 11:58:15 +08:00
Mygod deba1028c0 Remove unused sources in libsodium 2017-02-08 11:58:15 +08:00
Mygod 5559295beb Remove useless files in libsodium makefile 2017-02-08 11:58:15 +08:00
Mygod 41eec3e351 Use stable branch of libsodium 2017-02-08 11:58:15 +08:00
Mygod 56709385b1 Add missing AEAD ciphers 2017-02-08 11:58:15 +08:00
Mygod dc13249a1e Implement SIP002
https://github.com/shadowsocks/shadowsocks-org/issues/27
2017-02-08 11:58:15 +08:00
Mygod d579660682 Remove obsolete ciphers 2017-02-08 11:58:15 +08:00
Mygod 1e96a13ab1 Implement SIP004
https://github.com/shadowsocks/shadowsocks-org/issues/30
2017-02-08 11:58:15 +08:00
Mygod b71b27fbdf Use official repo of libsodium 2017-02-08 11:58:15 +08:00
Mygod 3fa8a0c90c Refine roundings for TrafficMonitor 2017-02-04 11:46:13 +08:00
Mygod f121ea2549 Prevent crashing when no market is installed 2017-02-02 21:51:52 +08:00
Mygod df1e40d60b Refine Subnet 2017-02-02 21:51:13 +08:00
Mygod ee08f02660 Use remote DNS for VPN 2017-02-02 21:49:31 +08:00
Mygod 7af26d1a4b Increase minimum sync time to let shadowsocks set up 2017-02-02 21:49:24 +08:00
Mygod 88319ecac7 Suggest to use fully qualified name for authorities in doc 2017-02-02 21:43:01 +08:00
Mygod b397ab2bbd Support pasting multiple lines 2017-02-02 21:27:35 +08:00
Mygod 33c087f13f Refine FAQ 2017-02-02 20:58:30 +08:00
Mygod 4996d4fc52 Add a known issue for Huawei's buggy ROM 2017-02-02 17:22:27 +08:00
Mygod 14fd86c716 Update android support library 2017-01-31 20:15:25 +08:00
Mygod 3b9a35d3e6 Merge branch 'beta' into develop 2017-01-31 19:30:37 +08:00
Mygod 3b93302539 Update dependencies 2017-01-31 01:37:25 +08:00
Max Lv 8de1dc1f7a Add signature 2017-01-24 22:59:26 +08:00
Mygod 55667c9e76 Add getOrDefault method for API 23- in plugin lib 2017-01-24 22:53:24 +08:00
Mygod e81c4f257e Bump plugin lib version 2017-01-24 22:52:54 +08:00
Mygod 61a926ff5e Update plugin doc 2017-01-24 22:42:24 +08:00
Mygod 8d1f2da1f0 Implement SIGTERM in jni-helper 2017-01-24 18:44:31 +08:00
Mygod 30c8c7e6eb Fix wrong thread error in binderDied 2017-01-24 16:58:55 +08:00
Mygod dadeffa903 Rename System to JniHelper to rename naming conflicts with
`java.lang.System`
2017-01-24 16:58:20 +08:00
Max Lv caf5731ff6 Update GFWList 2017-01-21 16:48:50 +08:00
Max Lv 437d8c3026 Revert "Revert "Speed up ndk-build from sbt-android""
This reverts commit bcbd860457.
2017-01-17 21:30:54 +08:00
Max Lv 44b47e6cd3 Update background for #1082 2017-01-16 16:18:18 +08:00
69 changed files with 8440 additions and 813 deletions
+2 -1
View File
@@ -29,7 +29,8 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails
1. Broken VPNService implementation, especially for IPv6;
2. Aggressive (or called broken) background service killing policy.
Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772)
* Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772)
* Fixes for Huawei: [#1091 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1091#issuecomment-276949836)
### How to pause Shadowsocks service?
+2 -1
View File
@@ -29,7 +29,8 @@ _Put an `x` inside the [ ] that applies._
* [ ] IPv6 route
* [ ] Per-App Proxy
* [ ] Bypass mode
* [ ] UDP Forwarding
* Remote DNS: 8.8.8.8
* [ ] DNS Forwarding
* Plugin configuration (if applicable):
* [ ] Auto Connect
* [ ] TCP Fast Open
+13
View File
@@ -9,3 +9,16 @@ local.sbt
mobile/src/main/libs
mobile/src/main/obj
mobile/src/main/jni/overture
mobile/src/overture/.deps
mobile/src/overture/bin
mobile/src/overture/pkg
mobile/src/overture/src/github.com/Sirupsen
mobile/src/overture/src/github.com/mattn
mobile/src/overture/src/github.com/miekg
mobile/src/overture/src/github.com/shawn1m
mobile/src/overture/src/github.com/tools
mobile/src/overture/src/golang.org
mobile/src/overture/src/gopkg.in
+8 -5
View File
@@ -17,10 +17,6 @@
path = mobile/src/main/jni/redsocks
url = https://github.com/shadowsocks/redsocks.git
branch = shadowsocks-android
[submodule "mobile/src/main/jni/pdnsd"]
path = mobile/src/main/jni/pdnsd
url = https://github.com/shadowsocks/pdnsd.git
branch = shadowsocks-android
[submodule "mobile/src/main/jni/mbedtls"]
path = mobile/src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls
@@ -29,10 +25,17 @@
url = https://android.googlesource.com/platform/external/pcre
[submodule "mobile/src/main/jni/libsodium"]
path = mobile/src/main/jni/libsodium
url = https://github.com/shadowsocks/libsodium.git
url = https://github.com/jedisct1/libsodium.git
branch = stable
[submodule "mobile/src/main/jni/libev"]
path = mobile/src/main/jni/libev
url = https://github.com/shadowsocks/libev.git
[submodule "mobile/src/main/jni/libudns"]
path = mobile/src/main/jni/libudns
url = https://github.com/shadowsocks/libudns.git
[submodule "mobile/src/overture/go"]
path = mobile/src/overture/go
url = https://github.com/shadowsocks/go.git
[submodule "mobile/src/overture/src/github.com/shadowsocks/overture"]
path = mobile/src/overture/src/github.com/shadowsocks/overture
url = https://github.com/shadowsocks/overture.git
-1
View File
@@ -1,4 +1,3 @@
-XX:MaxPermSize=512m
-Xms1g
-Xmx3g
-Xss2m
+3 -2
View File
@@ -5,8 +5,9 @@ jdk:
env:
global:
- NDK_VERSION=r12b
- NDK_VERSION=r14
- 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}
@@ -49,4 +50,4 @@ install:
fi
script:
- $SBTPATH/sbt android:package
- $SBTPATH/sbt go-build android:package
+6 -4
View File
@@ -12,15 +12,17 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* JDK 1.8
* SBT 0.13.0+
* Go 1.4+
* Android SDK
- Build Tools 25+
- Android Support Repository and Google Repository (see `build.sbt` for version)
* Android NDK r12b+
- Android NDK r14+
### BUILD
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
* Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk`
* (optional) Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk` (default: `$ANDROID_HOME/ndk-bundle`)
* 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
* Create `mobile/local.properties` from `mobile/local.properties.example` with your own key information
* Invoke the building like this
@@ -29,7 +31,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
git submodule update --init --recursive
# Build the App
sbt clean android:package-release
sbt clean go-build android:package-release
```
### TRANSLATE
@@ -50,7 +52,7 @@ Translators can go to [POEditor](https://poeditor.com/join/project/u5VHO9vhSf) t
<li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a></li>
<li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a></li>
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
<li>overture: <a href="https://github.com/shawn1m/overture/blob/master/LICENSE">MIT</a></li>
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a></li>
<li>libudns: <a href="https://github.com/shadowsocks/libudns/blob/master/COPYING.LGPL">LGPL</a></li>
+6 -4
View File
@@ -1,5 +1,5 @@
lazy val commonSettings = Seq(
scalaVersion := "2.11.8",
scalaVersion := "2.11.9",
dexMaxHeap := "4g",
organization := "com.github.shadowsocks",
@@ -11,14 +11,16 @@ lazy val commonSettings = Seq(
scalacOptions ++= "-target:jvm-1.7" :: "-Xexperimental" :: Nil,
ndkArgs := "-j" :: java.lang.Runtime.getRuntime.availableProcessors.toString :: Nil,
proguardVersion := "5.3.2",
proguardVersion := "5.3.3",
proguardCache := Seq(),
shrinkResources := true,
typedResources := false
typedResources := false,
resConfigs := Seq("ja", "ko", "ru", "zh-rCN", "zh-rTW")
)
val supportLibsVersion = "25.1.0"
val supportLibsVersion = "25.3.1"
lazy val root = Project(id = "shadowsocks-android", base = file("."))
.settings(commonSettings)
.aggregate(plugin, mobile)
+30 -14
View File
@@ -1,38 +1,54 @@
enablePlugins(AndroidApp)
enablePlugins(AndroidGms)
android.useSupportVectors
name := "shadowsocks"
version := "4.0.0"
versionCode := Some(175)
version := "4.1.6"
versionCode := Some(186)
proguardOptions ++=
"-keep class com.github.shadowsocks.System { *; }" ::
"-dontwarn com.evernote.android.job.gcm.**" ::
"-dontwarn com.evernote.android.job.util.GcmAvailableHelper" ::
"-dontwarn com.google.android.gms.internal.**" ::
"-dontwarn com.j256.ormlite.**" ::
"-dontwarn okio.**" ::
"-dontwarn org.xbill.**" ::
"-keep public class com.evernote.android.job.v21.PlatformJobService" ::
"-keep public class com.evernote.android.job.v14.PlatformAlarmService" ::
"-keep public class com.evernote.android.job.v14.PlatformAlarmReceiver" ::
"-keep public class com.evernote.android.job.JobBootReceiver" ::
"-keep public class com.evernote.android.job.JobRescheduleService" ::
"-keep class com.github.shadowsocks.JniHelper { *; }" ::
Nil
resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
val playServicesVersion = "10.0.1"
val playServicesVersion = "10.2.1"
resolvers += Resolver.jcenterRepo
libraryDependencies ++=
"com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" ::
"com.evernote" % "android-job" % "1.1.4" ::
"com.evernote" % "android-job" % "1.1.9" ::
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" ::
"com.google.android.gms" % "play-services-ads" % playServicesVersion ::
"com.google.android.gms" % "play-services-analytics" % playServicesVersion ::
"com.google.android.gms" % "play-services-gcm" % playServicesVersion ::
"com.google.firebase" % "firebase-config" % playServicesVersion ::
"com.j256.ormlite" % "ormlite-android" % "5.0" ::
"com.mikepenz" % "crossfader" % "1.5.0" ::
"com.mikepenz" % "fastadapter" % "2.1.5" ::
"com.mikepenz" % "iconics-core" % "2.8.2" ::
"com.mikepenz" % "materialdrawer" % "5.8.1" ::
"com.mikepenz" % "materialize" % "1.0.0" ::
"com.squareup.okhttp3" % "okhttp" % "3.5.0" ::
"com.mikepenz" % "fastadapter" % "2.5.2" ::
"com.mikepenz" % "iconics-core" % "2.8.3" ::
"com.mikepenz" % "materialdrawer" % "5.9.0" ::
"com.mikepenz" % "materialize" % "1.0.1" ::
"com.squareup.okhttp3" % "okhttp" % "3.7.0" ::
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
"dnsjava" % "dnsjava" % "2.1.7" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
"dnsjava" % "dnsjava" % "2.1.8" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201704021214" ::
"net.glxn.qrgen" % "android" % "2.0" ::
Nil
lazy val goBuild = TaskKey[Unit]("go-build", "Build go and overture")
goBuild := {
Process(Seq("mobile/src/overture/make.bash", minSdkVersion.value)) ! streams.value.log match {
case 0 => // Success!
case n => sys.error(s"Native build script exit code: $n")
}
}
+50
View File
@@ -0,0 +1,50 @@
{
"project_info": {
"project_number": "261400168171",
"firebase_url": "https://admob-app-id-3330146721.firebaseio.com",
"project_id": "admob-app-id-3330146721",
"storage_bucket": "admob-app-id-3330146721.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:261400168171:android:dbdd6331c434162f",
"android_client_info": {
"package_name": "com.github.shadowsocks"
}
},
"oauth_client": [
{
"client_id": "261400168171-sfik8o3pj7e243583olorh7s5974vab1.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.github.shadowsocks",
"certificate_hash": "58a90f84cfe99d4280aec677c9a1292fae131677"
}
},
{
"client_id": "261400168171-g7aelv5bu012ojr7dod7lq09c9anjimh.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCee3fAad7nb3YsxeUO9mqqHFfAvsSCbVs"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}
+2
View File
@@ -144,6 +144,8 @@
android:process=":bg"/>
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
android:process=":bg"/>
<service android:name="com.evernote.android.job.JobRescheduleService"
android:process=":bg"/>
<receiver android:name="com.evernote.android.job.v14.PlatformAlarmReceiver"
android:process=":bg"/>
<receiver android:name="com.evernote.android.job.JobBootReceiver"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
127.0.0.1 localhost
+2 -2
View File
@@ -275,7 +275,7 @@ img {
<body>
<p>A <a href="http://shadowsocks.org">Shadowsocks</a> client for Android, written in Scala.</p>
<p>Copyright (C) 2017 by Max Lv <a href="&#x6d;&#97;&#x69;&#108;&#116;&#111;&#x3a;&#x6d;&#97;x&#46;&#99;.&#108;&#118;&#x40;&#103;&#109;&#97;&#x69;&#108;&#46;&#x63;&#x6f;&#109;">&#x6d;&#97;&#x78;.&#x63;&#x2e;&#108;v&#64;&#x67;&#109;&#x61;&#x69;&#108;&#x2e;&#99;&#111;&#109;</a></p>
<p>Copyright (C) 2017 by Mygod Studio <a href="contact-shadowsocks-android@mygod.be">contact-shadowsocks-android@mygod.be</a></p>
<p>Copyright (C) 2017 by Mygod Studio <a href="mailto:contact-shadowsocks-android@mygod.be">contact-shadowsocks-android@mygod.be</a></p>
<p>This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@@ -296,7 +296,7 @@ along with this program. If not, see <a href="http://www.gnu.org/licenses/">http
<li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a></li>
<li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a></li>
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
<li>overture: <a href="https://github.com/shawn1m/overture/blob/master/LICENSE">MIT</a></li>
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a></li>
<li>libudns: <a href="https://github.com/shadowsocks/libudns/blob/master/COPYING.LGPL">LGPL</a></li>
@@ -38,12 +38,35 @@
package com.github.shadowsocks;
public class System {
static {
java.lang.System.loadLibrary("system");
}
import android.os.Build;
import android.system.ErrnoException;
public static native int exec(String cmd);
public static native int sendfd(int fd, String path);
public static native void jniclose(int fd);
public class JniHelper {
static {
System.loadLibrary("jni-helper");
}
@Deprecated // Use Process.destroy() since API 24
public static void sigtermCompat(Process process) throws Exception {
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
int errno = sigterm(process);
if (errno != 0) throw Build.VERSION.SDK_INT >= 21
? new ErrnoException("kill", errno) : new Exception("kill failed: " + errno);
}
@Deprecated // only implemented for before API 24
public static boolean waitForCompat(Process process, long millis) throws Exception {
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
final Object mutex = getExitValueMutex(process);
synchronized (mutex) {
if (getExitValue(process) == null) mutex.wait(millis);
return getExitValue(process) != null;
}
}
private static native int sigterm(Process process);
private static native Integer getExitValue(Process process);
private static native Object getExitValueMutex(Process process);
public static native int sendFd(int fd, String path);
public static native void close(int fd);
}
+65 -128
View File
@@ -26,115 +26,34 @@ include $(CLEAR_VARS)
SODIUM_SOURCE := \
crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c \
crypto_auth/crypto_auth.c \
crypto_auth/hmacsha256/auth_hmacsha256_api.c \
crypto_auth/hmacsha256/cp/hmac_hmacsha256.c \
crypto_auth/hmacsha256/cp/verify_hmacsha256.c \
crypto_auth/hmacsha512/auth_hmacsha512_api.c \
crypto_auth/hmacsha512/cp/hmac_hmacsha512.c \
crypto_auth/hmacsha512/cp/verify_hmacsha512.c \
crypto_auth/hmacsha512256/auth_hmacsha512256_api.c \
crypto_auth/hmacsha512256/cp/hmac_hmacsha512256.c \
crypto_auth/hmacsha512256/cp/verify_hmacsha512256.c \
crypto_box/crypto_box.c \
crypto_box/crypto_box_easy.c \
crypto_box/crypto_box_seal.c \
crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305_api.c \
crypto_box/curve25519xsalsa20poly1305/ref/after_curve25519xsalsa20poly1305.c \
crypto_box/curve25519xsalsa20poly1305/ref/before_curve25519xsalsa20poly1305.c \
crypto_box/curve25519xsalsa20poly1305/ref/box_curve25519xsalsa20poly1305.c \
crypto_box/curve25519xsalsa20poly1305/ref/keypair_curve25519xsalsa20poly1305.c \
crypto_core/hsalsa20/ref2/core_hsalsa20.c \
crypto_core/hsalsa20/core_hsalsa20_api.c \
crypto_core/salsa20/ref/core_salsa20.c \
crypto_core/salsa20/core_salsa20_api.c \
crypto_generichash/crypto_generichash.c \
crypto_generichash/blake2/generichash_blake2_api.c \
crypto_generichash/blake2/ref/blake2-impl.h \
crypto_generichash/blake2/ref/blake2.h \
crypto_generichash/blake2/ref/blake2b-compress-ref.c \
crypto_generichash/blake2/ref/blake2b-load-sse2.h \
crypto_generichash/blake2/ref/blake2b-load-sse41.h \
crypto_generichash/blake2/ref/blake2b-ref.c \
crypto_generichash/blake2/ref/blake2b-round.h \
crypto_generichash/blake2/ref/generichash_blake2b.c \
crypto_hash/crypto_hash.c \
crypto_hash/sha256/hash_sha256_api.c \
crypto_hash/sha256/cp/hash_sha256.c \
crypto_hash/sha512/hash_sha512_api.c \
crypto_hash/sha512/cp/hash_sha512.c \
crypto_onetimeauth/crypto_onetimeauth.c \
crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c \
crypto_core/hchacha20/core_hchacha20.c \
crypto_core/salsa/ref/core_salsa_ref.c \
crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
crypto_generichash/blake2b/ref/blake2b-ref.c \
crypto_generichash/blake2b/ref/generichash_blake2b.c \
crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \
crypto_onetimeauth/poly1305/onetimeauth_poly1305.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna32.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna64.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna.c \
crypto_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c \
crypto_pwhash/scryptsalsa208sha256/crypto_scrypt.h \
crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c \
crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c \
crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.h \
crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c \
crypto_pwhash/scryptsalsa208sha256/sysendian.h \
crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c \
crypto_scalarmult/crypto_scalarmult.c \
crypto_pwhash/crypto_pwhash.c \
crypto_pwhash/argon2/argon2-core.c \
crypto_pwhash/argon2/argon2.c \
crypto_pwhash/argon2/argon2-encoding.c \
crypto_pwhash/argon2/argon2-fill-block-ref.c \
crypto_pwhash/argon2/blake2b-long.c \
crypto_pwhash/argon2/pwhash_argon2i.c \
crypto_scalarmult/curve25519/scalarmult_curve25519.c \
crypto_scalarmult/curve25519/scalarmult_curve25519.h \
crypto_secretbox/crypto_secretbox.c \
crypto_secretbox/crypto_secretbox_easy.c \
crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305_api.c \
crypto_shorthash/crypto_shorthash.c \
crypto_shorthash/siphash24/shorthash_siphash24_api.c \
crypto_shorthash/siphash24/ref/shorthash_siphash24.c \
crypto_sign/crypto_sign.c \
crypto_sign/ed25519/ref10/base.h \
crypto_sign/ed25519/ref10/base2.h \
crypto_sign/ed25519/sign_ed25519_api.c \
crypto_sign/ed25519/ref10/keypair.c \
crypto_sign/ed25519/ref10/open.c \
crypto_sign/ed25519/ref10/obsolete.c \
crypto_sign/ed25519/ref10/sign.c \
crypto_stream/crypto_stream.c \
crypto_stream/chacha20/stream_chacha20.c \
crypto_stream/chacha20/stream_chacha20.h \
crypto_stream/chacha20/ref/stream_chacha20_ref.h \
crypto_stream/chacha20/ref/stream_chacha20_ref.c \
crypto_stream/salsa20/stream_salsa20_api.c \
crypto_stream/xsalsa20/stream_xsalsa20_api.c \
crypto_stream/xsalsa20/ref/stream_xsalsa20.c \
crypto_stream/xsalsa20/ref/xor_xsalsa20.c \
crypto_verify/16/verify_16_api.c \
crypto_verify/16/ref/verify_16.c \
crypto_verify/32/verify_32_api.c \
crypto_verify/32/ref/verify_32.c \
crypto_verify/64/verify_64_api.c \
crypto_verify/64/ref/verify_64.c \
crypto_stream/chacha20/ref/chacha20_ref.c \
crypto_stream/salsa20/stream_salsa20.c \
crypto_stream/salsa20/ref/salsa20_ref.c \
crypto_verify/sodium/verify.c \
randombytes/randombytes.c \
randombytes/sysrandom/randombytes_sysrandom.c \
sodium/core.c \
sodium/runtime.c \
sodium/utils.c \
sodium/version.c
SODIUM_SOURCE += \
crypto_scalarmult/curve25519/ref10/x25519_ref10.c
SODIUM_SOURCE += \
crypto_pwhash/argon2/argon2-core.c \
crypto_pwhash/argon2/argon2.c \
crypto_pwhash/argon2/argon2-encoding.c \
crypto_pwhash/argon2/blake2b-long.c \
crypto_pwhash/argon2/argon2-fill-block-ref.c \
crypto_pwhash/argon2/pwhash_argon2i.c \
crypto_pwhash/argon2/argon2-fill-block-ssse3.c
SODIUM_SOURCE += \
crypto_stream/salsa20/ref/stream_salsa20_ref.c \
crypto_stream/salsa20/ref/xor_salsa20_ref.c
SODIUM_SOURCE += \
randombytes/sysrandom/randombytes_sysrandom.c
LOCAL_MODULE := sodium
LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/include \
@@ -180,7 +99,7 @@ LIBEVENT_SOURCES := \
LOCAL_MODULE := event
LOCAL_SRC_FILES := $(addprefix libevent/, $(LIBEVENT_SOURCES))
LOCAL_CFLAGS := -O2 -I$(LOCAL_PATH)/libevent \
LOCAL_CFLAGS := -O2 -D_EVENT_HAVE_ARC4RANDOM -I$(LOCAL_PATH)/libevent \
-I$(LOCAL_PATH)/libevent/include \
include $(BUILD_STATIC_LIBRARY)
@@ -200,6 +119,22 @@ LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
include $(BUILD_STATIC_LIBRARY)
########################################################
## libbloom
########################################################
include $(CLEAR_VARS)
BLOOM_SOURCE := bloom.c murmur2/MurmurHash2.c
LOCAL_MODULE := libbloom
LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom/murmur2
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libbloom/, $(BLOOM_SOURCE))
include $(BUILD_STATIC_LIBRARY)
########################################################
## libipset
########################################################
@@ -306,32 +241,17 @@ LOCAL_CFLAGS := -O2 -std=gnu99 -DUSE_IPTABLES \
include $(BUILD_SHARED_EXECUTABLE)
########################################################
## pdnsd
########################################################
include $(CLEAR_VARS)
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)/libancillary
LOCAL_STATIC_LIBRARIES := libancillary
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_EXECUTABLE)
########################################################
## shadowsocks-libev local
########################################################
include $(CLEAR_VARS)
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 \
plugin.c \
SHADOWSOCKS_SOURCES := local.c \
cache.c udprelay.c utils.c netutils.c json.c jconf.c \
acl.c http.c tls.c rule.c \
crypto.c aead.c stream.c base64.c \
plugin.c ppbloom.c \
android.c
LOCAL_MODULE := ss-local
@@ -345,13 +265,14 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_LOCAL \
-I$(LOCAL_PATH)/mbedtls/include \
-I$(LOCAL_PATH)/pcre \
-I$(LOCAL_PATH)/libudns \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-I$(LOCAL_PATH)/libev
LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libudns \
LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom libudns \
libsodium libancillary libpcre
LOCAL_LDLIBS := -llog
@@ -364,7 +285,11 @@ include $(BUILD_SHARED_EXECUTABLE)
include $(CLEAR_VARS)
SHADOWSOCKS_SOURCES := tunnel.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c android.c plugin.c
SHADOWSOCKS_SOURCES := tunnel.c \
cache.c udprelay.c utils.c netutils.c json.c jconf.c \
crypto.c aead.c stream.c base64.c \
plugin.c ppbloom.c \
android.c
LOCAL_MODULE := ss-tunnel
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
@@ -374,30 +299,31 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_TUNNEL \
-I$(LOCAL_PATH)/libancillary \
-I$(LOCAL_PATH)/include \
-I$(LOCAL_PATH)/libudns \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \
-I$(LOCAL_PATH)/mbedtls/include \
-I$(LOCAL_PATH)/libev \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-I$(LOCAL_PATH)/include/shadowsocks-libev
LOCAL_STATIC_LIBRARIES := libev libmbedtls libsodium libcork libudns libancillary
LOCAL_STATIC_LIBRARIES := libev libmbedtls libsodium libcork libbloom libudns libancillary
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_EXECUTABLE)
########################################################
## system
## jni-helper
########################################################
include $(CLEAR_VARS)
LOCAL_MODULE:= system
LOCAL_MODULE:= jni-helper
LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libancillary
LOCAL_SRC_FILES:= system.cpp
LOCAL_SRC_FILES:= jni-helper.cpp
LOCAL_LDLIBS := -ldl -llog
@@ -553,6 +479,17 @@ LOCAL_SRC_FILES := $(addprefix pcre/, $(libpcre_src_files))
include $(BUILD_STATIC_LIBRARY)
########################################################
## overture
########################################################
include $(CLEAR_VARS)
LOCAL_MODULE := overture
LOCAL_SRC_FILES := overture/$(TARGET_ARCH_ABI)/liboverture.so
include $(PREBUILT_SHARED_LIBRARY)
# Import cpufeatures
$(call import-module,android/cpufeatures)
+1 -1
View File
@@ -1,4 +1,4 @@
APP_ABI := armeabi-v7a x86
APP_ABI := armeabi-v7a arm64-v8a x86
APP_PLATFORM := android-19
APP_STL := stlport_static
NDK_TOOLCHAIN_VERSION := clang
+198
View File
@@ -0,0 +1,198 @@
#define LOG_TAG "JniHelper"
#include "jni.h"
#include <android/log.h>
#include <sys/system_properties.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ancillary.h>
#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0)
#define THROW(env, clazz, msg) do { env->ThrowNew(env->FindClass(clazz), msg); } while (0)
static jclass ProcessImpl;
static jfieldID ProcessImpl_pid, ProcessImpl_exitValue, ProcessImpl_exitValueMutex;
static int sdk_version() {
char version[PROP_VALUE_MAX + 1];
__system_property_get("ro.build.version.sdk", version);
return atoi(version);
}
jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jobject process) {
if (!env->IsInstanceOf(process, ProcessImpl)) {
THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
return -1;
}
jint pid = env->GetIntField(process, ProcessImpl_pid);
// Suppress "No such process" errors. We just want the process killed. It's fine if it's already killed.
return kill(pid, SIGTERM) == -1 && errno != ESRCH ? errno : 0;
}
jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject thiz, jobject process) {
if (!env->IsInstanceOf(process, ProcessImpl)) {
THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
return NULL;
}
return env->GetObjectField(process, ProcessImpl_exitValue);
}
jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, jobject thiz, jobject process) {
if (!env->IsInstanceOf(process, ProcessImpl)) {
THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
return NULL;
}
return env->GetObjectField(process, ProcessImpl_exitValueMutex);
}
void Java_com_github_shadowsocks_jnihelper_close(JNIEnv *env, jobject thiz, jint fd) {
close(fd);
}
jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd;
struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0);
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
LOGE("socket() failed: %s (socket fd = %d)\n", strerror(errno), fd);
return (jint)-1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_str, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
LOGE("connect() failed: %s (fd = %d)\n", strerror(errno), fd);
close(fd);
return (jint)-1;
}
if (ancil_send_fd(fd, tun_fd)) {
LOGE("ancil_send_fd: %s", strerror(errno));
close(fd);
return (jint)-1;
}
close(fd);
env->ReleaseStringUTFChars(path, sock_str);
return 0;
}
static const char *classPathName = "com/github/shadowsocks/JniHelper";
static JNINativeMethod method_table[] = {
{ "close", "(I)V",
(void*) Java_com_github_shadowsocks_jnihelper_close },
{ "sendFd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_jnihelper_sendfd },
{ "sigterm", "(Ljava/lang/Process;)I",
(void*) Java_com_github_shadowsocks_jnihelper_sigterm },
{ "getExitValue", "(Ljava/lang/Process;)Ljava/lang/Integer;",
(void*) Java_com_github_shadowsocks_jnihelper_getExitValue },
{ "getExitValueMutex", "(Ljava/lang/Process;)Ljava/lang/Object;",
(void*) Java_com_github_shadowsocks_jnihelper_getExitValueMutex }
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName, method_table,
sizeof(method_table) / sizeof(method_table[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
THROW(env, "java/lang/RuntimeException", "GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
THROW(env, "java/lang/RuntimeException", "registerNativeMethods failed");
goto bail;
}
if (sdk_version() < 24) {
if (!(ProcessImpl = env->FindClass("java/lang/ProcessManager$ProcessImpl"))) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl not found");
goto bail;
}
ProcessImpl = (jclass) env->NewGlobalRef((jobject) ProcessImpl);
if (!(ProcessImpl_pid = env->GetFieldID(ProcessImpl, "pid", "I"))) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl.pid not found");
goto bail;
}
if (!(ProcessImpl_exitValue = env->GetFieldID(ProcessImpl, "exitValue", "Ljava/lang/Integer;"))) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl.exitValue not found");
goto bail;
}
if (!(ProcessImpl_exitValueMutex = env->GetFieldID(ProcessImpl, "exitValueMutex", "Ljava/lang/Object;"))) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl.exitValueMutex not found");
goto bail;
}
}
result = JNI_VERSION_1_6;
bail:
return result;
}
-159
View File
@@ -1,159 +0,0 @@
#define LOG_TAG "Shadowsocks"
#include "jni.h"
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ancillary.h>
#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0)
jint Java_com_github_shadowsocks_system_exec(JNIEnv *env, jobject thiz, jstring cmd) {
const char *cmd_str = env->GetStringUTFChars(cmd, 0);
pid_t pid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
env->ReleaseStringUTFChars(cmd, cmd_str);
return -1;
}
if (pid > 0) {
env->ReleaseStringUTFChars(cmd, cmd_str);
return pid;
}
execl("/system/bin/sh", "sh", "-c", cmd_str, NULL);
env->ReleaseStringUTFChars(cmd, cmd_str);
return 1;
}
void Java_com_github_shadowsocks_system_jniclose(JNIEnv *env, jobject thiz, jint fd) {
close(fd);
}
jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd;
struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0);
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
LOGE("socket() failed: %s (socket fd = %d)\n", strerror(errno), fd);
return (jint)-1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_str, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
LOGE("connect() failed: %s (fd = %d)\n", strerror(errno), fd);
close(fd);
return (jint)-1;
}
if (ancil_send_fd(fd, tun_fd)) {
LOGE("ancil_send_fd: %s", strerror(errno));
close(fd);
return (jint)-1;
}
close(fd);
env->ReleaseStringUTFChars(path, sock_str);
return 0;
}
static const char *classPathName = "com/github/shadowsocks/System";
static JNINativeMethod method_table[] = {
{ "jniclose", "(I)V",
(void*) Java_com_github_shadowsocks_system_jniclose },
{ "sendfd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_system_sendfd },
{ "exec", "(Ljava/lang/String;)I",
(void*) Java_com_github_shadowsocks_system_exec }
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName, method_table,
sizeof(method_table) / sizeof(method_table[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
LOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
LOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_4;
bail:
return result;
}
@@ -10,6 +10,5 @@
<EditText android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
android:lines="1"/>
android:inputType="textNoSuggestions|textMultiLine"/>
</LinearLayout>
Binary file not shown.
+37 -43
View File
@@ -7,7 +7,7 @@
<string name="profile">"プロファイル"</string>
<string name="profile_summary">"新規作成または既存ファイルを開く"</string>
<string name="nat">"NAT モード (デバックのみ)"</string>
<string name="nat_summary">"VPNモードからNATモードに切り替えるにはROOT権限が必要です"</string>
<string name="nat_summary">"VPN モードから NAT モードに切り替えるには ROOT 権限が必要です"</string>
<string name="remote_dns">"リモートDNS"</string>
<string name="stat_summary">"送信済み: \t%3$s\t↑\t%1$s/s
受信済み: \t%4$s\t↓\t%2$s/s"</string>
@@ -19,7 +19,6 @@
<string name="connection_test_error_status_code">"ステータスコード無効(#%d)"</string>
<!-- proxy category -->
<string name="proxy_cat">"サーバー設定"</string>
<string name="profile_name">"サーバー名"</string>
<string name="proxy">"サーバーアドレス"</string>
<string name="remote_port">"リモートポート"</string>
@@ -28,11 +27,8 @@
<string name="enc_method">"暗号化方式"</string>
<!-- feature category -->
<string name="feature_cat">"ファンクション設定"</string>
<string name="ipv6">"IPv6プロキシ"</string>
<string name="ipv6_summary">"リモートサーバーにIPv6パケットを転送"</string>
<string name="onetime_auth">"ワンタイム認証"</string>
<string name="onetime_auth_summary">"ワンタイム認証を使用する"</string>
<string name="ipv6">"IPv6 プロキシ"</string>
<string name="ipv6_summary">"リモートサーバーに IPv6 パケットを転送"</string>
<string name="route_list">"プロキシ方式"</string>
<string name="route_entry_gfwlist">"中国本土からアクセス不可なアドレス以外を迂回する"</string>
<string name="proxied_apps">"アプリ別のプロキシを使用"</string>
@@ -42,28 +38,25 @@
<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">"使用するには 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>
<string name="udp_dns">"DNS 転送"</string>
<string name="udp_dns_summary">"DNS プロトコルでリモードサーバーにパケットを転送"</string>
<!-- notification category -->
<string name="forward_success">"バックグラウンドでサービスを開始しました"</string>
<string name="invalid_server">"サーバーが無効です"</string>
<string name="service_failed">"リモートサーバーに接続できません"</string>
<string name="method_unsafe">"注意:現在の暗号化方式は安全ではありません"</string>
<string name="nat_deprecated">"注意:Android 5.0及びそれ以降のバージョンではNATモードの使用はお勧めしません"</string>
<string name="nat_no_root">"NATモードはROOT権限が必要です"</string>
<string name="switch_to_vpn">"VPNモードに切り替える"</string>
<string name="nat_deprecated">"注意:Android 5.0 及びそれ以降のバージョンでは NAT モードの使用はお勧めしません"</string>
<string name="nat_no_root">"NAT モードは ROOT 権限が必要です"</string>
<string name="switch_to_vpn">"VPN モードに切り替える"</string>
<string name="stop">"中止"</string>
<string name="stopping">"停止中……"</string>
<string name="vpn_error">"バックグランドサービスの起動に失敗しました:%s"</string>
<string name="reboot_required">"VPNサービスの起動に失敗しました、デバイスの再起動を試みて下さい"</string>
<string name="profile_invalid_input">"有効なデータが見つかりません"</string>
<string name="reboot_required">"VPN サービスの起動に失敗しました、デバイスの再起動を試みて下さい"</string>
<string name="profile_invalid_input">"有効なプロファイルが見つかりません"</string>
<!-- alert category -->
<string name="yes">"はい"</string>
<string name="no">"いいえ"</string>
<string name="close">"閉じる"</string>
<string name="profile_empty">"プロファイルを選択して下さい"</string>
<string name="proxy_empty">"サーバアドレスやパスワードの入力が必要です"</string>
@@ -74,9 +67,10 @@
<!-- menu category -->
<string name="profiles">"プロファイル"</string>
<string name="settings">"オプション設定"</string>
<string name="faq">"質問"</string>
<string name="faq">"よくある質問"</string>
<string name="recovery">"リセット"</string>
<string name="about">"本アプリについて"</string>
<string name="about_title">"Shadowsocks %s"</string>
<string name="edit">"編集"</string>
<string name="share">"共有"</string>
<string name="add_profile">"プロファイルを追加"</string>
@@ -85,24 +79,22 @@
<string name="action_import">"クリップボードからインポート"</string>
<string name="action_export_msg">"クリップボードの出力に成功しました"</string>
<string name="action_export_err">"クリップボードの出力に失敗しました"</string>
<string name="action_import_msg">"インポートは成功しました"</string>
<string name="action_import_err">"インポートは失敗しました"</string>
<string name="action_import_msg">"インポートに成功しました"</string>
<string name="action_import_err">"インポートに失敗しました"</string>
<!-- profile -->
<string name="profile_config">"プロファイル編集"</string>
<string name="unsaved_changes_prompt">"編集は保存されておりません、保存しますか?"</string>
<string name="apply">"適応する"</string>
<string name="delete">"削除する"</string>
<string name="delete_confirm_prompt">"このプロファイルを削除しますか?"</string>
<string name="share_qr_nfc">"QRコード/NFC"</string>
<string name="add_profile_dialog">"シャドーソックスにプロファイルを追加しますか"</string>
<string name="add_profile_methods_scan_qr_code">"QRコードを読み取る"</string>
<string name="add_profile_methods_manual_settings">"手動でセットアップ"</string>
<string name="delete">"削除"</string>
<string name="delete_confirm_prompt">"このプロファイルを削除しますか"</string>
<string name="share_qr_nfc">"QR コード / NFC"</string>
<string name="add_profile_dialog">"Shadowsocks用プロファイルを追加しますか"</string>
<string name="add_profile_methods_scan_qr_code">"QR コードを読み取る"</string>
<string name="add_profile_methods_manual_settings">"手動設定"</string>
<string name="add_profile_scanner_not_installed">"Zxingに準拠したQRコードスキャンアプリをインストールして下さい"</string>
<plurals name="removed">
<item quantity="other">"削除済み"</item>
</plurals>
<string name="undo">"撤回"</string>
<string name="undo">"元に戻す"</string>
<!-- tasker -->
<string name="toggle_service_state">"サービスを開始"</string>
@@ -114,28 +106,30 @@
<!-- status -->
<string name="sent">"送信済み:"</string>
<string name="received">"受信済み:"</string>
<string name="connecting">"接続中..."</string>
<string name="connecting">"接続中…"</string>
<string name="vpn_connected">"接続済み、タップして接続状況をチェック"</string>
<string name="nat_connected">"接続済み"</string>
<string name="not_connected">"接続されませんでした"</string>
<!-- kcp -->
<string name="kcp_summary">"KCP プロトコル を有効にする"</string>
<string name="kcp_port">"KCP ポート"</string>
<string name="kcp_port_summary">"%d(リモート KCPTUN サーバーのポート番号)"</string>
<string name="kcpcli">"KCP パラメータ"</string>
<string name="not_connected">"未接続"</string>
<!-- acl -->
<string name="custom_rules">"カスタムルール"</string>
<string name="action_selection">"選択"</string>
<string name="action_add_rule">"ルールを追加"</string>
<string name="action_add_rule">"ルールを追加…"</string>
<string name="edit_rule">"ルールを編集"</string>
<string name="route_entry_all">"すべて"</string>
<string name="route_entry_bypass_lan">"LANアドレスを迂回する"</string>
<string name="route_entry_bypass_lan">"LAN アドレスを迂回する"</string>
<string name="route_entry_bypass_chn">"中国本土のアドレスを迂回する"</string>
<string name="route_entry_bypass_lan_chn">"LAN及び中国本土のアドレスを迂回する"</string>
<string name="route_entry_bypass_lan_chn">"LAN 及び中国本土のアドレスを迂回する"</string>
<string name="route_entry_chinalist">"中国本土のアドレス以外を迂回する"</string>
<string name="proxied_apps_summary_v21">"選択したアプリにプロキシを設定する"</string>
<string name="acl_rule_templates_generic">"サブネット/ホストネームPCREパターン"</string>
<string name="acl_rule_templates_domain">"ドメイン及びサブドメイン"</string>
<string name="acl_rule_templates_generic">"サブネット/ホスト名 PCRE パターン"</string>
<string name="acl_rule_templates_domain">"ドメイン及び全てのサブドメイン"</string>
<!-- plugin -->
<string name="plugin">"プラグイン"</string>
<string name="plugin_configure">"設定..."</string>
<string name="plugin_disabled">"無効"</string>
<string name="plugin_unknown">"不明なプラグイン"</string>
<string name="plugin_untrusted">"警告:このプラグインは信頼されていないソースからの可能性があります"</string>
<string name="profile_plugin">"プラグイン"</string>
</resources>
+48
View File
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="quick_toggle">"켜기/끄기"</string>
<!-- global/misc category -->
<string name="misc_cat">"기타"</string>
<string name="profile">"프로필"</string>
<string name="profile_summary">"다른 프로필로 전환하거나 새 프로필 추가"</string>
<string name="nat">"NAT 모드 (지원 중단)"</string>
<string name="nat_summary">"VPN 모드 대신 NAT 모드를 사용합니다. 루트 권한이 필요합니다."</string>
<string name="remote_dns">"원격 DNS"</string>
<string name="stat_summary">"송신: \t\t\t\t\t%3$s\t↑\t%1$s/s
수신: \t%4$s\t↓\t%2$s/s "</string>
<string name="stat_profiles">"%1$s↑\t%2$s↓"</string>
<string name="traffic_summary">"%1$s/s↑\t%2$s/s↓"</string>
<plurals name="bytes">
<item quantity="other">"바이트"</item>
</plurals>
<string name="connection_test_pending">"인터넷 연결 상태 검사하기"</string>
<string name="connection_test_testing">"검사 중…"</string>
<string name="connection_test_available">"성공: %dms"</string>
<string name="connection_test_error">"인터넷 연결을 찾을 수 없습니다: %s"</string>
<string name="connection_test_fail">"인터넷에 연결할 수 없습니다"</string>
<string name="connection_test_error_status_code">"오류 코드: #%d"</string>
<!-- proxy category -->
<string name="profile_name">"프로필 이름"</string>
<string name="proxy">"서버 주소"</string>
<string name="remote_port">"원격 포트"</string>
<string name="port">"로컬 포트"</string>
<string name="sitekey">"비밀번호"</string>
<string name="enc_method">"암호화 방법"</string>
<!-- feature category -->
<string name="ipv6">"IPv6 라우팅"</string>
<string name="ipv6_summary">"IPv6 트래픽도 원격으로 리다이렉트 합니다"</string>
<string name="route_list">"라우팅 대상"</string>
<string name="proxied_apps">"원하는 앱만 프락시 적용하기"</string>
<string name="proxied_apps_summary">"선택한 앱에만 프락시를 적용합니다. Android 4.x 이하에서는 NAT 모드를 써야 합니다."</string>
<string name="on">"활성화"</string>
<string name="bypass_apps">"선택된 앱들만 프록시 적용 제외하기"</string>
<string name="bypass_apps_summary">"이 옵션을 활성화하면 선택된 앱들은 프락시를 사용하지 않습니다"</string>
<string name="auto_connect">"자동 접속"</string>
<string name="auto_connect_summary">"장치가 켜질 때 Shadowsocks를 자동으로 활성화합니다"</string>
<string name="tcp_fastopen_summary">"루트 권한이 필요합니다"</string>
<string name="tcp_fastopen_summary_unsupported">"지원하지 않는 버전의 커널입니다: %s &lt; 3.7.1"</string>
<string name="udp_dns">"DNS 포워딩"</string>
</resources>
+10 -26
View File
@@ -21,7 +21,6 @@
<string name="connection_test_error_status_code">"Код ошибки: #%d"</string>
<!-- proxy category -->
<string name="proxy_cat">"Настройки Сервера"</string>
<string name="profile_name">"Имя Профиля"</string>
<string name="proxy">"Сервер"</string>
<string name="remote_port">"Удалённый порт"</string>
@@ -30,17 +29,8 @@
<string name="enc_method">"Метод Шифрования"</string>
<!-- feature category -->
<string name="feature_cat">"Дополнительные Настройки"</string>
<string name="ipv6">"IPv6 Маршрут"</string>
<string name="ipv6_summary">"Перенаправлять IPv6 траффик на удалённый сервер"</string>
<string name="onetime_auth">"Единоразовая Аутентификация"</string>
<string name="onetime_auth_summary">"Включить единоразовую аутентификацию"</string>
<string name="http_proxy">"HTTP Прокси"</string>
<string name="http_proxy_summary">"Локальный HTTP прокси"</string>
<string name="dns_proxy">"DNS Прокси"</string>
<string name="dns_proxy_summary">"Перенаправлять DNS запросы на удалённый сервер"</string>
<string name="auto_set_gfwlist">"CHN Маршрут"</string>
<string name="auto_set_gfwlist_summary">"Игнорировать все сайты находящиеся в Китае"</string>
<string name="route_list">"Маршрут"</string>
<string name="route_entry_gfwlist">"Список GFW"</string>
<string name="proxied_apps">"Прокси для выбранных приложений"</string>
@@ -52,27 +42,25 @@
<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>
<string name="udp_dns">"Проброс DNS"</string>
<string name="udp_dns_summary">"Пробрасывать DNS пакеты на удалённый сервер"</string>
<!-- notification category -->
<string name="forward_success">"Shadowsocks запущен."</string>
<string name="invalid_server">"Неправильное имя сервера"</string>
<string name="service_failed">"Ошибка при подключении к удалённому серверу"</string>
<string name="method_unsafe">"ВНИМАНИЕ:В настоящее время метод шифрования является небезопасным"</string>
<string name="nat_deprecated">"ВНИМАНИЕ:режим NAT не работает в Android 5.0"</string>
<string name="nat_no_root">"Режим NAT требует наличия ROOT прав"</string>
<string name="switch_to_vpn">"Переключить на режим VPN"</string>
<string name="stop">"Остановить"</string>
<string name="stopping">"Останавливается…"</string>
<string name="vpn_error">"%s"</string>
<string name="reboot_required">"Сбой запуска VPN сервиса. Требуется перезагрузка Вашего устройства."</string>
<!-- alert category -->
<string name="close">"Закрыть"</string>
<string name="proxy_empty">"Прокси/Пароль не должны быть пустыми"</string>
<string name="connect">"Подключить"</string>
<string name="initializing">"Инициализация…"</string>
<string name="recovering">"Сброс…"</string>
<string name="loading">"Загрузка…"</string>
<string name="remove_profile">"Удалить этот профиль %s?"</string>
<!-- menu category -->
@@ -82,10 +70,6 @@
<string name="about">"О приложении"</string>
<string name="about_title">"Shadowsocks %s"</string>
<string name="share">"Поделиться"</string>
<string name="flush_dnscache">"Очистить кэш DNS"</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>
<string name="action_apply_all">"Применить настройки для всех профилей"</string>
<string name="action_export">"Экспортировать в буфер обмена"</string>
@@ -97,6 +81,12 @@
<string name="add_profile_dialog">"Добавить этот Профиль Shadowsocks?"</string>
<string name="add_profile_methods_scan_qr_code">"Сканировать QR код"</string>
<string name="add_profile_methods_manual_settings">"Настройка вручную"</string>
<plurals name="removed">
<item quantity="one">"Удалён"</item>
<item quantity="few">"%d элементы удалены"</item>
<item quantity="many">"%d элементы удалены"</item>
<item quantity="other">"%d элементы удалены"</item>
</plurals>
<string name="undo">"Вернуть"</string>
<!-- tasker -->
@@ -109,12 +99,6 @@
<!-- status -->
<string name="sent">"Отправлено:"</string>
<string name="received">"Получено:"</string>
<!-- kcp -->
<string name="kcp_summary">"Включить Shadowsocks над KCPTUN"</string>
<string name="kcp_port">"KCP порт"</string>
<string name="kcp_port_summary">"%d (KCPTUN омер порта удалённого сервера)"</string>
<string name="kcpcli">"KCP параметры"</string>
<string name="route_entry_all">"Все"</string>
<string name="route_entry_bypass_lan">"Обход LAN"</string>
<string name="route_entry_bypass_chn">"Обходной Китай"</string>
+11 -18
View File
@@ -20,7 +20,6 @@
<string name="connection_test_error_status_code">"状态码无效(#%d)"</string>
<!-- proxy category -->
<string name="proxy_cat">"服务器设置"</string>
<string name="profile_name">"配置名称"</string>
<string name="proxy">"服务器"</string>
<string name="remote_port">"远程端口"</string>
@@ -29,11 +28,8 @@
<string name="enc_method">"加密方法"</string>
<!-- feature category -->
<string name="feature_cat">"功能设置"</string>
<string name="ipv6">"IPv6 路由"</string>
<string name="ipv6_summary">"向远程服务器转发 IPv6 流量"</string>
<string name="onetime_auth">"一次性认证"</string>
<string name="onetime_auth_summary">"启用一次性认证"</string>
<string name="route_list">"路由"</string>
<string name="route_entry_gfwlist">"仅代理中国大陆无法访问的地址"</string>
<string name="proxied_apps">"分应用代理"</string>
@@ -45,14 +41,13 @@
<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="udp_dns">"UDP 转发"</string>
<string name="udp_dns_summary">"由远程服务器转发 UDP 协议的数据包"</string>
<string name="udp_dns">"DNS 转发"</string>
<string name="udp_dns_summary">"通过 UDP 将所有 DNS 查询全部转发至远程服务器"</string>
<!-- notification category -->
<string name="forward_success">"后台服务已开始运行。"</string>
<string name="invalid_server">"服务器名无效"</string>
<string name="service_failed">"无法连接远程服务器"</string>
<string name="method_unsafe">"警告:当前使用的加密算法不安全"</string>
<string name="nat_deprecated">"警告:在 Android 5.0 及更高版本中不建议使用 NAT 模式"</string>
<string name="nat_no_root">"NAT 模式需要 ROOT 权限"</string>
<string name="switch_to_vpn">"切换到 VPN 模式"</string>
@@ -63,14 +58,12 @@
<string name="profile_invalid_input">"未找到有效的配置文件。"</string>
<!-- alert category -->
<string name="yes">"是"</string>
<string name="no">"否"</string>
<string name="close">"关闭"</string>
<string name="profile_empty">"请选择配置文件"</string>
<string name="proxy_empty">"代理服务器地址及密码不能为空"</string>
<string name="connect">"连接"</string>
<string name="recovering">"重置中…"</string>
<string name="remove_profile">"删除此配置文件「%s」?"</string>
<string name="remove_profile">"删除此配置文件“%s”?"</string>
<!-- menu category -->
<string name="profiles">"配置文件"</string>
@@ -92,8 +85,6 @@
<!-- profile -->
<string name="profile_config">"配置文件设置"</string>
<string name="unsaved_changes_prompt">"保存修改吗?"</string>
<string name="apply">"应用"</string>
<string name="delete">"删除"</string>
<string name="delete_confirm_prompt">"您确定要删除此配置文件?"</string>
<string name="share_qr_nfc">"二维码 / NFC"</string>
@@ -121,12 +112,6 @@
<string name="nat_connected">"已连接"</string>
<string name="not_connected">"未连接"</string>
<!-- kcp -->
<string name="kcp_summary">"启用 KCP 协议"</string>
<string name="kcp_port">"KCP 端口"</string>
<string name="kcp_port_summary">"%d(远端 KCPTUN 服务器的端口号)"</string>
<string name="kcpcli">"KCP 参数"</string>
<!-- acl -->
<string name="custom_rules">"自定义规则"</string>
<string name="action_selection">"选择…"</string>
@@ -140,4 +125,12 @@
<string name="proxied_apps_summary_v21">"为应用程序分别设置代理"</string>
<string name="acl_rule_templates_generic">"子网/域名 PCRE 正则表达式"</string>
<string name="acl_rule_templates_domain">"域名及其子域名"</string>
<!-- plugin -->
<string name="plugin">"插件"</string>
<string name="plugin_configure">"配置…"</string>
<string name="plugin_disabled">"禁用"</string>
<string name="plugin_unknown">"未知插件 %s"</string>
<string name="plugin_untrusted">"警告:该插件似乎并非来自已知的可信源。"</string>
<string name="profile_plugin">"插件:%s"</string>
</resources>
+2 -17
View File
@@ -19,7 +19,6 @@
<string name="connection_test_error_status_code">"錯誤碼: #%d"</string>
<!-- proxy category -->
<string name="proxy_cat">"伺服器設定"</string>
<string name="profile_name">"設定檔名稱"</string>
<string name="proxy">"伺服器"</string>
<string name="remote_port">"遠端連接埠"</string>
@@ -28,11 +27,8 @@
<string name="enc_method">"加密方法"</string>
<!-- feature category -->
<string name="feature_cat">"功能設定"</string>
<string name="ipv6">"IPv6 路由"</string>
<string name="ipv6_summary">"向遠端重新導向 IPv6 流量"</string>
<string name="onetime_auth">"單次驗證"</string>
<string name="onetime_auth_summary">"啟用單次驗證"</string>
<string name="route_list">"路由"</string>
<string name="route_entry_gfwlist">"GFW List"</string>
<string name="proxied_apps">"個別應用程式的 Proxy"</string>
@@ -44,14 +40,13 @@
<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>
<string name="udp_dns">"DNS 轉送"</string>
<string name="udp_dns_summary">"使用 UDP 向遠端轉送 DNS 封包"</string>
<!-- notification category -->
<string name="forward_success">"Shadowsocks 已啟動。"</string>
<string name="invalid_server">"伺服器名稱無效"</string>
<string name="service_failed">"連線至遠端伺服器失敗"</string>
<string name="method_unsafe">"警告:目前使用的加密方法不安全"</string>
<string name="nat_deprecated">"警告:自 Android 5.0 開始, NAT 模式已過時"</string>
<string name="nat_no_root">"NAT 模式需要 ROOT 權限"</string>
<string name="switch_to_vpn">"切換至 VPN 模式"</string>
@@ -62,8 +57,6 @@
<string name="profile_invalid_input">"未找到有效的設定檔資料。"</string>
<!-- alert category -->
<string name="yes">"是"</string>
<string name="no">"否"</string>
<string name="close">"關閉"</string>
<string name="profile_empty">"請選擇設定檔"</string>
<string name="proxy_empty">"Proxy 或密碼不可以空白"</string>
@@ -91,8 +84,6 @@
<!-- profile -->
<string name="profile_config">"設定檔設定"</string>
<string name="unsaved_changes_prompt">"要儲存變更嗎?"</string>
<string name="apply">"套用"</string>
<string name="delete">"刪除"</string>
<string name="delete_confirm_prompt">"您確定要移除這個設定檔嗎?"</string>
<string name="share_qr_nfc">"QR 碼 / NFC"</string>
@@ -120,12 +111,6 @@
<string name="nat_connected">"已連線"</string>
<string name="not_connected">"未連線"</string>
<!-- kcp -->
<string name="kcp_summary">"啟用 KCP 協定"</string>
<string name="kcp_port">"KCP 連接埠"</string>
<string name="kcp_port_summary">"%d(遠端 KCPTUN 伺服器的連接埠號碼)"</string>
<string name="kcpcli">"KCP 參數 (CLI)"</string>
<!-- acl -->
<string name="custom_rules">"自訂規則"</string>
<string name="action_selection">"選擇…"</string>
+10 -4
View File
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="enc_method_entry">
<item>TABLE</item>
<item>RC4</item>
<item>RC4-MD5</item>
<item>AES-128-CFB</item>
<item>AES-192-CFB</item>
@@ -17,11 +15,14 @@
<item>SALSA20</item>
<item>CHACHA20</item>
<item>CHACHA20-IETF</item>
<item>AES-128-GCM</item>
<item>AES-192-GCM</item>
<item>AES-256-GCM</item>
<item>CHACHA20-IETF-POLY1305</item>
<item>XCHACHA20-IETF-POLY1305</item>
</string-array>
<string-array name="enc_method_value">
<item>table</item>
<item>rc4</item>
<item>rc4-md5</item>
<item>aes-128-cfb</item>
<item>aes-192-cfb</item>
@@ -36,6 +37,11 @@
<item>salsa20</item>
<item>chacha20</item>
<item>chacha20-ietf</item>
<item>aes-128-gcm</item>
<item>aes-192-gcm</item>
<item>aes-256-gcm</item>
<item>chacha20-ietf-poly1305</item>
<item>xchacha20-ietf-poly1305</item>
</string-array>
<string-array name="route_entry">
+3 -7
View File
@@ -4,8 +4,7 @@
<string name="app_name">Shadowsocks</string>
<string name="quick_toggle">Toggle</string>
<!-- global/misc category -->
<string name="misc_cat">Misc</string>
<!-- misc -->
<string name="profile">Profile</string>
<string name="profile_summary">Switch to another profile or add new profiles</string>
<string name="nat">NAT mode (deprecated)</string>
@@ -36,8 +35,6 @@
<!-- feature category -->
<string name="ipv6">IPv6 Route</string>
<string name="ipv6_summary">Redirect IPv6 traffic to remote</string>
<string name="onetime_auth">One-time Authentication</string>
<string name="onetime_auth_summary">Enable one-time authentication</string>
<string name="route_list">Route</string>
<string name="route_entry_all">All</string>
<string name="route_entry_bypass_lan">Bypass LAN</string>
@@ -55,14 +52,13 @@
<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>
<string name="udp_dns">DNS Forwarding</string>
<string name="udp_dns_summary">Forward all DNS requests to remote</string>
<!-- notification category -->
<string name="forward_success">Shadowsocks started.</string>
<string name="invalid_server">Invalid server name</string>
<string name="service_failed">Failed to connect the remote server</string>
<string name="method_unsafe">WARNING: The current encryption method is unsafe</string>
<string name="nat_deprecated">WARNING: NAT mode has been deprecated since Android 5.0</string>
<string name="nat_no_root">NAT mode requires ROOT permission</string>
<string name="switch_to_vpn">Switch to VPN mode</string>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<defaultsMap>
<entry>
<key>proxy_url</key>
<value>https://www.socks123.pw/get.php</value>
</entry>
</defaultsMap>
+5 -8
View File
@@ -2,13 +2,14 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
<be.mygod.preference.EditTextPreference
android:key="profileName"
android:summary="%s"
android:title="@string/profile_name"/>
<be.mygod.preference.PreferenceCategory
android:title="@string/proxy_cat">
<be.mygod.preference.EditTextPreference
android:key="profileName"
android:summary="%s"
android:title="@string/profile_name"/>
<be.mygod.preference.EditTextPreference
android:key="proxy"
android:summary="%s"
@@ -36,10 +37,6 @@
android:entryValues="@array/enc_method_value"
android:summary="%s"
android:title="@string/enc_method"/>
<SwitchPreference
android:key="isAuth"
android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth"/>
</be.mygod.preference.PreferenceCategory>
@@ -21,10 +21,11 @@
package com.github.shadowsocks
import java.io.{File, IOException}
import java.net.InetAddress
import java.util
import java.util.concurrent.TimeUnit
import java.util.{Timer, TimerTask}
import java.net.InetAddress
import java.net.Inet6Address
import android.app.Service
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
@@ -39,11 +40,11 @@ import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.plugin.{PluginConfiguration, PluginManager, PluginOptions}
import com.github.shadowsocks.utils._
import okhttp3.{Dns, FormBody, OkHttpClient, Request}
import org.json.JSONObject
import org.json.{JSONArray, JSONObject}
import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.collection.JavaConversions._
import scala.util.Random
trait BaseService extends Service {
@@ -124,12 +125,9 @@ trait BaseService extends Service {
} else true
def connect() {
if (profile.host == "198.199.101.152") {
val holder = app.containerHolder
val container = holder.getContainer
val url = container.getString("proxy_url")
val sig = Utils.getSignature(this)
profile.name = profile.getName // save original name before it's (possibly) overwritten by IP addresses
if (profile.host == "198.199.101.152") {
val client = new OkHttpClient.Builder()
.dns(hostname => Utils.resolve(hostname, enableIPv6 = false) match {
case Some(ip) => util.Arrays.asList(InetAddress.getByName(ip))
@@ -140,10 +138,10 @@ trait BaseService extends Service {
.readTimeout(30, TimeUnit.SECONDS)
.build()
val requestBody = new FormBody.Builder()
.add("sig", sig)
.add("sig", Utils.getSignature(this))
.build()
val request = new Request.Builder()
.url(url)
.url(app.remoteConfig.getString("proxy_url"))
.post(requestBody)
.build()
@@ -183,9 +181,6 @@ trait BaseService extends Service {
changeState(State.CONNECTING)
if (profile.isMethodUnsafe)
handler.post(() => Toast.makeText(this, R.string.method_unsafe, Toast.LENGTH_LONG).show())
Utils.ThrowableFuture(try connect() catch {
case _: NameNotResolvedException => stopRunner(stopService = true, getString(R.string.invalid_server))
case _: NullConnectionException => stopRunner(stopService = true, getString(R.string.reboot_required))
@@ -277,7 +272,6 @@ trait BaseService extends Service {
override def onCreate() {
super.onCreate()
app.refreshContainerHolder()
app.updateAssets()
}
@@ -302,34 +296,69 @@ trait BaseService extends Service {
}
}
def getBlackList: String = {
val default = getString(R.string.black_list)
try {
val container = app.containerHolder.getContainer
val update = container.getString("black_list_lite")
val list = if (update == null || update.isEmpty) default else update
"exclude = " + list + ";"
} catch {
case _: Exception => "exclude = " + default + ";"
}
}
protected def buildPluginCommandLine(): ArrayBuffer[String] = {
val result = ArrayBuffer(pluginPath)
if (TcpFastOpen.sendEnabled) result += "--fast-open"
result
}
protected final def buildShadowsocksConfig(file: String): String = {
val config = new JSONObject()
.put("server", profile.host)
.put("server_port", profile.remotePort)
.put("password", profile.password)
.put("method", profile.method)
if (profile.auth) config.put("auth", true)
if (pluginPath != null) config
.put("plugin", Commandline.toString(buildPluginCommandLine()))
.put("plugin_opts", plugin.toString)
IOUtils.writeString(new File(getFilesDir, file), config.toString)
file
}
protected final def buildOvertureConfig(file: String): String = {
val config = new JSONObject()
.put("BindAddress", ":" + (profile.localPort + 53))
.put("RedirectIPv6Record", true)
.put("DomainBase64Decode", true)
.put("HostsFile", "hosts")
.put("MinimumTTL", 3600)
.put("CacheSize", 4096)
def makeDns(name: String, address: String, edns: Boolean = true) = {
val dns = new JSONObject()
.put("Name", name)
.put("Address", (Utils.parseNumericAddress(address) match {
case _: Inet6Address => '[' + address + ']'
case _ => address
}) + ":53")
.put("Timeout", 6)
.put("EDNSClientSubnet", new JSONObject().put("Policy", "disable"))
if (edns) dns
.put("Protocol", "tcp")
.put("Socks5Address", "127.0.0.1:" + profile.localPort)
else dns.put("Protocol", "udp")
dns
}
val remoteDns = new JSONArray(profile.remoteDns.split(",").zipWithIndex.map {
case (dns, i) => makeDns("UserDef-" + i, dns.trim)
})
profile.route match {
case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES => config
.put("PrimaryDNS", new JSONArray(Array(
makeDns("Primary-1", "119.29.29.29", edns = false),
makeDns("Primary-2", "114.114.114.114", edns = false)
)))
.put("AlternativeDNS", remoteDns)
.put("IPNetworkFile", "china_ip_list.txt")
.put("DomainFile", "gfwlist.txt")
case Acl.CHINALIST => config
.put("PrimaryDNS", new JSONArray().put(makeDns("Primary", "119.29.29.29")))
.put("AlternativeDNS", remoteDns)
case _ => config
.put("PrimaryDNS", remoteDns)
// no need to setup AlternativeDNS in Acl.ALL/BYPASS_LAN mode
.put("OnlyPrimaryDNS", true)
}
IOUtils.writeString(new File(getFilesDir, file), config.toString)
file
}
}
@@ -24,6 +24,7 @@ import java.io._
import java.lang.System.currentTimeMillis
import java.util.concurrent.Semaphore
import android.os.Build
import android.util.Log
import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils.Commandline
@@ -89,7 +90,7 @@ class GuardedProcess(cmd: String*) {
} catch {
case _: InterruptedException =>
if (BuildConfig.DEBUG) Log.d(TAG, "thread interrupt, destroy process: " + Commandline.toString(cmd))
process.destroy()
destroyProcess()
case e: IOException => ioException = e
} finally semaphore.release()
}, "GuardThread-" + cmd.head)
@@ -105,16 +106,24 @@ class GuardedProcess(cmd: String*) {
def destroy() {
isDestroyed = true
guardThread.interrupt()
process.destroy()
destroyProcess()
try guardThread.join() catch {
case _: InterruptedException =>
}
}
private def destroyProcess() {
if (Build.VERSION.SDK_INT < 24) {
JniHelper.sigtermCompat(process)
JniHelper.waitForCompat(process, 500)
}
process.destroy()
}
def restart() {
this.synchronized {
isRestart = true
process.destroy()
destroyProcess()
}
}
@@ -84,11 +84,6 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
private var txRateText: TextView = _
private var rxRateText: TextView = _
private var currentFragment: ToolbarFragment = _
private lazy val profilesFragment = new ProfilesFragment()
private lazy val customRulesFragment = new CustomRulesFragment()
private lazy val globalSettingsFragment = new GlobalSettingsFragment()
private lazy val aboutFragment = new AboutFragment()
private lazy val customTabsIntent = new CustomTabsIntent.Builder()
.setToolbarColor(ContextCompat.getColor(this, R.color.material_primary_500))
.build()
@@ -172,11 +167,11 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
private def addDisableNatToSnackbar(snackbar: Snackbar) = snackbar.setAction(R.string.switch_to_vpn, (_ =>
if (state == State.STOPPED) app.editor.putBoolean(Key.isNAT, false).apply()): View.OnClickListener)
override def binderDied() {
override def binderDied(): Unit = handler.post(() => {
detachService()
app.crashRecovery()
attachService(callback)
}
})
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = resultCode match {
case Activity.RESULT_OK => bgService.use(app.profileId)
@@ -255,7 +250,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
val tf = Typefaces.get(this, "fonts/Iceland.ttf")
if (tf != null) title.setTypeface(tf)
if (savedInstanceState == null) displayFragment(profilesFragment)
if (savedInstanceState == null) displayFragment(new ProfilesFragment)
statusText = findViewById(R.id.status).asInstanceOf[TextView]
txText = findViewById(R.id.tx).asInstanceOf[TextView]
txRateText = findViewById(R.id.txRate).asInstanceOf[TextView]
@@ -364,14 +359,13 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
}
private def displayFragment(fragment: ToolbarFragment) {
currentFragment = fragment
getFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss()
drawer.closeDrawer()
}
override def onItemClick(view: View, position: Int, drawerItem: IDrawerItem[_, _ <: ViewHolder]): Boolean = {
drawerItem.getIdentifier match {
case DRAWER_PROFILES => displayFragment(profilesFragment)
case DRAWER_PROFILES => displayFragment(new ProfilesFragment)
case DRAWER_RECOVERY =>
app.track("GlobalConfigFragment", "reset")
if (bgService != null) bgService.use(-1)
@@ -384,19 +378,20 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
app.copyAssets()
handler.sendEmptyMessage(0)
}
case DRAWER_GLOBAL_SETTINGS => displayFragment(globalSettingsFragment)
case DRAWER_GLOBAL_SETTINGS => displayFragment(new GlobalSettingsFragment)
case DRAWER_ABOUT =>
app.track(TAG, "about")
displayFragment(aboutFragment)
displayFragment(new AboutFragment)
case DRAWER_FAQ => launchUrl(getString(R.string.faq_url))
case DRAWER_CUSTOM_RULES => displayFragment(customRulesFragment)
case DRAWER_CUSTOM_RULES => displayFragment(new CustomRulesFragment)
case _ => // ignore
}
true // unexpected cases will throw exception
}
protected override def onResume() {
super.onResume()
app.refreshContainerHolder()
app.remoteConfig.fetch()
state match {
case State.STOPPING | State.CONNECTING =>
case _ => hideCircle()
@@ -408,12 +403,14 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
setListeningForBandwidth(true)
}
override def onBackPressed(): Unit =
if (drawer.isDrawerOpen) drawer.closeDrawer() else if (!currentFragment.onBackPressed())
if (currentFragment != profilesFragment) {
displayFragment(profilesFragment)
override def onBackPressed(): Unit = if (drawer.isDrawerOpen) drawer.closeDrawer() else {
val currentFragment = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
if (!currentFragment.onBackPressed())
if (currentFragment.isInstanceOf[ProfilesFragment]) super.onBackPressed() else {
displayFragment(new ProfilesFragment)
drawer.setSelection(DRAWER_PROFILES)
} else super.onBackPressed()
}
}
override def onStop() {
setListeningForBandwidth(false)
@@ -352,13 +352,21 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
def onMenuItemClick(item: MenuItem): Boolean = item.getItemId match {
case R.id.action_scan_qr_code =>
def installScanner() = {
Toast.makeText(getActivity, R.string.add_profile_scanner_not_installed, Toast.LENGTH_LONG).show()
try startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=tw.com.quickmark"))) catch {
case exc: ActivityNotFoundException => exc.printStackTrace()
}
}
try startActivityForResult(new Intent("com.google.zxing.client.android.SCAN")
.addCategory(Intent.CATEGORY_DEFAULT)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_DOCUMENT),
REQUEST_SCAN_QR_CODE) catch {
case _: ActivityNotFoundException =>
Toast.makeText(getActivity, R.string.add_profile_scanner_not_installed, Toast.LENGTH_LONG).show()
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=tw.com.quickmark")))
case _: ActivityNotFoundException => installScanner()
case e: SecurityException =>
e.printStackTrace()
app.track(e)
installScanner()
}
true
case R.id.action_import =>
@@ -21,9 +21,7 @@
package com.github.shadowsocks
import java.io.{File, FileOutputStream, IOException}
import java.util
import java.util.Locale
import java.util.concurrent.TimeUnit
import android.annotation.SuppressLint
import android.app.Application
@@ -39,8 +37,8 @@ import com.github.shadowsocks.database.{DBHelper, Profile, ProfileManager}
import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders, StandardExceptionParser, Tracker}
import com.google.android.gms.common.api.ResultCallback
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
import com.google.firebase.FirebaseApp
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.j256.ormlite.logger.LocalLog
import eu.chainfire.libsuperuser.Shell
@@ -61,8 +59,7 @@ object ShadowsocksApplication {
class ShadowsocksApplication extends Application {
import ShadowsocksApplication._
final val SIG_FUNC = "getSignature"
var containerHolder: ContainerHolder = _
lazy val remoteConfig = FirebaseRemoteConfig.getInstance()
lazy val tracker: Tracker = GoogleAnalytics.getInstance(this).newTracker(R.xml.tracker)
lazy val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
lazy val editor: SharedPreferences.Editor = settings.edit
@@ -149,35 +146,16 @@ class ShadowsocksApplication extends Application {
if (!BuildConfig.DEBUG) java.lang.System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
checkChineseLocale(getResources.getConfiguration)
val tm = TagManager.getInstance(this)
val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container)
val callback = new ResultCallback[ContainerHolder] {
override def onResult(holder: ContainerHolder) {
if (!holder.getStatus.isSuccess) {
return
}
containerHolder = holder
val container = holder.getContainer
container.registerFunctionCallMacroCallback(SIG_FUNC,
(functionName: String, parameters: util.Map[String, AnyRef]) => {
if (functionName == SIG_FUNC) {
Utils.getSignature(getApplicationContext)
}
null
})
}
}
pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
FirebaseApp.initializeApp(this)
remoteConfig.setDefaults(R.xml.default_configs);
remoteConfig.fetch().addOnCompleteListener(task => if (task.isSuccessful) remoteConfig.activateFetched())
JobManager.create(this).addJobCreator(DonaldTrump)
TcpFastOpen.enabled(settings.getBoolean(Key.tfo, TcpFastOpen.sendEnabled))
}
def refreshContainerHolder() {
val holder = app.containerHolder
if (holder != null) holder.refresh()
}
def crashRecovery() {
val cmd = new ArrayBuffer[String]()
@@ -197,15 +175,17 @@ class ShadowsocksApplication extends Application {
def copyAssets() {
val assetManager = getAssets
var files: Array[String] = null
try files = assetManager.list("acl") catch {
case e: IOException =>
Log.e(TAG, e.getMessage)
app.track(e)
for (dir <- List("acl", "overture")) {
var files: Array[String] = null
try files = assetManager.list(dir) catch {
case e: IOException =>
Log.e(TAG, e.getMessage)
app.track(e)
}
if (files != null) for (file <- files) autoClose(assetManager.open(dir + "/" + file))(in =>
autoClose(new FileOutputStream(new File(getFilesDir, file)))(out =>
IOUtils.copy(in, out)))
}
if (files != null) for (file <- files) autoClose(assetManager.open("acl/" + file))(in =>
autoClose(new FileOutputStream(new File(getFilesDir, file)))(out =>
IOUtils.copy(in, out)))
editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
}
@@ -49,7 +49,7 @@ class ShadowsocksNatService extends BaseService {
var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _
var redsocksProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _
var overtureProcess: GuardedProcess = _
var su: Shell.Interactive = _
def startShadowsocksDaemon() {
@@ -74,7 +74,7 @@ class ShadowsocksNatService extends BaseService {
"-t", "10",
"-b", "127.0.0.1",
"-l", (profile.localPort + 63).toString,
"-L", if (profile.remoteDns == null) "8.8.8.8:53" else profile.remoteDns + ":53",
"-L", profile.remoteDns.split(",").head.trim + ":53",
"-c", buildShadowsocksConfig("ss-tunnel-nat.conf"))
if (profile.udpdns) cmd.append("-u")
@@ -83,21 +83,8 @@ class ShadowsocksNatService extends BaseService {
}
def startDnsDaemon() {
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
IOUtils.writeString(new File(getFilesDir, "pdnsd-nat.conf"), profile.route match {
case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
"127.0.0.1", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
getBlackList, reject, profile.localPort + 63, reject)
case Acl.CHINALIST =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
"127.0.0.1", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
"", reject, profile.localPort + 63, reject)
case _ =>
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, reject)
})
pdnsdProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/libpdnsd.so", "-c", "pdnsd-nat.conf")
overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
"-c", buildOvertureConfig("overture-nat.conf"))
.start()
}
@@ -111,11 +98,13 @@ class ShadowsocksNatService extends BaseService {
/** Called when the activity is first created. */
def handleConnection() {
startDNSTunnel()
startRedsocksDaemon()
startShadowsocksDaemon()
if (!profile.udpdns) startDnsDaemon()
if (!profile.udpdns)
startDnsDaemon()
else
startDNSTunnel()
setupIptables()
@@ -143,9 +132,9 @@ class ShadowsocksNatService extends BaseService {
redsocksProcess.destroy()
redsocksProcess = null
}
if (pdnsdProcess != null) {
pdnsdProcess.destroy()
pdnsdProcess = null
if (overtureProcess != null) {
overtureProcess.destroy()
overtureProcess = null
}
su.addCommand("iptables -t nat -F OUTPUT")
@@ -215,7 +204,7 @@ class ShadowsocksNatService extends BaseService {
AclSyncJob.schedule(profile.route)
changeState(State.CONNECTED)
notification = new ShadowsocksNotification(this, profile.getName, true)
notification = new ShadowsocksNotification(this, profile.name, true)
}
override def stopRunner(stopService: Boolean, msg: String = null) {
@@ -23,14 +23,13 @@ package com.github.shadowsocks
import java.io.File
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.ShadowsocksApplication.app
import com.github.shadowsocks.acl.{Acl, AclSyncJob}
import com.github.shadowsocks.acl.{Acl, AclSyncJob, Subnet}
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils._
@@ -46,8 +45,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
private var notification: ShadowsocksNotification = _
var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _
var overtureProcess: GuardedProcess = _
var tun2socksProcess: GuardedProcess = _
override def onBind(intent: Intent): IBinder = {
@@ -95,17 +93,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess.destroy()
sslocalProcess = null
}
if (sstunnelProcess != null) {
sstunnelProcess.destroy()
sstunnelProcess = null
}
if (tun2socksProcess != null) {
tun2socksProcess.destroy()
tun2socksProcess = null
}
if (pdnsdProcess != null) {
pdnsdProcess.destroy()
pdnsdProcess = null
if (overtureProcess != null) {
overtureProcess.destroy()
overtureProcess = null
}
}
@@ -144,21 +138,20 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.route != Acl.ALL && profile.route != Acl.CUSTOM_RULES)
AclSyncJob.schedule(profile.route)
notification = new ShadowsocksNotification(this, profile.getName)
notification = new ShadowsocksNotification(this, profile.name)
}
/** Called when the activity is first created. */
def handleConnection() {
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
startShadowsocksDaemon()
if (!profile.udpdns) {
startDnsDaemon()
startDnsTunnel()
}
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
}
override protected def buildPluginCommandLine(): ArrayBuffer[String] = super.buildPluginCommandLine() += "-V"
@@ -166,13 +159,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
def startShadowsocksDaemon() {
val cmd = ArrayBuffer[String](getApplicationInfo.nativeLibraryDir + "/libss-local.so",
"-V",
"-u",
"-b", "127.0.0.1",
"-l", profile.localPort.toString,
"-t", "600",
"-c", buildShadowsocksConfig("ss-local-vpn.conf"))
if (profile.udpdns) cmd += "-u"
if (profile.route != Acl.ALL) {
cmd += "--acl"
cmd += Acl.getFile(profile.route).getAbsolutePath
@@ -183,36 +175,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess = new GuardedProcess(cmd: _*).start()
}
def startDnsTunnel(): Unit =
sstunnelProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/libss-tunnel.so",
"-V",
"-t", "10",
"-b", "127.0.0.1",
"-l", (profile.localPort + 63).toString,
"-L" , if (profile.remoteDns == null) "8.8.8.8:53" else profile.remoteDns + ":53",
"-c", buildShadowsocksConfig("ss-tunnel-vpn.conf")).start()
def startDnsDaemon() {
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
IOUtils.writeString(new File(getFilesDir, "pdnsd-vpn.conf"), profile.route match {
case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
"0.0.0.0", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
getBlackList, reject, profile.localPort + 63, reject)
case Acl.CHINALIST =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
"0.0.0.0", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
"", reject, profile.localPort + 63, reject)
case _ =>
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
"0.0.0.0", profile.localPort + 53, profile.localPort + 63, reject)
})
val cmd = Array(getApplicationInfo.nativeLibraryDir + "/libpdnsd.so", "-c", "pdnsd-vpn.conf")
pdnsdProcess = new GuardedProcess(cmd: _*).start()
overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
"-c", buildOvertureConfig("overture-vpn.conf"), "-V")
.start()
}
@SuppressLint(Array("NewApi"))
def startVpn(): Int = {
val builder = new Builder()
@@ -221,7 +189,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
.setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
builder.addDnsServer("8.8.8.8")
builder.addDnsServer("8.8.8.8") // It's fake DNS for tun2socks, not the real remote DNS
if (profile.ipv6) {
builder.addAddress(PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "1"), 126)
@@ -249,15 +217,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.route == Acl.ALL || profile.route == Acl.BYPASS_CHN) {
builder.addRoute("0.0.0.0", 0)
} else {
val privateList = getResources.getStringArray(R.array.bypass_private_route)
privateList.foreach(cidr => {
val addr = cidr.split('/')
builder.addRoute(addr(0), addr(1).toInt)
getResources.getStringArray(R.array.bypass_private_route).foreach(cidr => {
val subnet = Subnet.fromString(cidr)
builder.addRoute(subnet.address.getHostAddress, subnet.prefixSize)
})
profile.remoteDns.split(",").map(dns => Utils.parseNumericAddress(dns.trim)).foreach(dns =>
builder.addRoute(dns, dns.getAddress.length << 3))
}
builder.addRoute("8.8.0.0", 16)
conn = builder.establish()
if (conn == null) throw new NullConnectionException
@@ -275,9 +242,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.ipv6)
cmd += ("--netif-ip6addr", PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "2"))
if (profile.udpdns)
cmd += "--enable-udprelay"
else
cmd += "--enable-udprelay"
if (!profile.udpdns)
cmd += ("--dnsgw", "%s:%d".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"),
profile.localPort + 53))
@@ -291,7 +258,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
var tries = 1
while (tries < 5) {
Thread.sleep(1000 * tries)
if (System.sendfd(fd, new File(getFilesDir, "sock_path").getAbsolutePath) != -1) {
if (JniHelper.sendFd(fd, new File(getFilesDir, "sock_path").getAbsolutePath) != -1) {
return true
}
tries += 1
@@ -92,7 +92,7 @@ class ShadowsocksVpnThread(service: ShadowsocksVpnService) extends Thread {
val ret = service.protect(fd)
// Trick to close file decriptor
System.jniclose(fd)
JniHelper.close(fd)
if (ret) {
output.write(0)
@@ -34,9 +34,10 @@ import scala.io.Source
*/
object AclSyncJob {
final val TAG = "AclSyncJob"
final val MAX_RESCHEDULE = 3
def schedule(route: String): Int = new JobRequest.Builder(AclSyncJob.TAG + ':' + route)
.setExecutionWindow(1, TimeUnit.DAYS.toMillis(28))
.setExecutionWindow(TimeUnit.SECONDS.toMillis(10), TimeUnit.DAYS.toMillis(28))
.setRequirementsEnforced(true)
.setRequiredNetworkType(JobRequest.NetworkType.UNMETERED)
.setRequiresCharging(true)
@@ -45,6 +46,8 @@ object AclSyncJob {
}
class AclSyncJob(route: String) extends Job {
var rescheduled = 0
override def onRunJob(params: Params): Result = {
try {
//noinspection JavaAccessorMethodCalledAsEmptyParen
@@ -54,7 +57,11 @@ class AclSyncJob(route: String) extends Job {
} catch {
case e: IOException =>
e.printStackTrace()
Result.RESCHEDULE
rescheduled += 1
if (rescheduled < AclSyncJob.MAX_RESCHEDULE)
Result.RESCHEDULE
else
Result.FAILURE
case e: Exception => // unknown failures, probably shouldn't retry
e.printStackTrace()
Result.FAILURE
@@ -20,6 +20,7 @@ import com.github.shadowsocks.widget.UndoSnackbarManager
import com.github.shadowsocks.{MainActivity, R, ToolbarFragment}
import scala.collection.mutable
import scala.io.Source
/**
* @author Mygod
@@ -144,8 +145,12 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
apply()
index
} else -1
def addToProxy(input: String): Int = try addSubnet(Subnet.fromString(input)) catch {
case _: IllegalArgumentException => addHostname(input)
def addToProxy(input: String): Int = {
val acl = new Acl().fromSource(Source.fromString(input), defaultBypass = true)
var result = -1
for (hostname <- acl.proxyHostnames) result = addHostname(hostname)
if (acl.bypass) for (subnet <- acl.subnets) result = addSubnet(subnet)
result
}
def addFromTemplate(template: Int, text: CharSequence): Int = template match {
case GENERIC => addToProxy(text.toString)
@@ -298,13 +303,7 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
.create().show()
true
case R.id.action_import =>
try {
val items = clipboard.getPrimaryClip.getItemAt(0).getText.toString.split("\n")
if (items.nonEmpty) {
items.foreach(adapter.addToProxy)
return true
}
} catch {
try if (adapter.addToProxy(clipboard.getPrimaryClip.getItemAt(0).getText.toString) != -1) return true catch {
case _: Exception =>
}
Snackbar.make(getActivity.findViewById(R.id.snackbar), R.string.action_import_err, Snackbar.LENGTH_LONG).show()
@@ -1,6 +1,7 @@
package com.github.shadowsocks.acl
import java.net.{Inet4Address, Inet6Address, InetAddress}
import java.net.InetAddress
import java.util.Objects
import com.github.shadowsocks.utils.Utils
@@ -9,16 +10,11 @@ import com.github.shadowsocks.utils.Utils
*/
@throws[IllegalArgumentException]
class Subnet(val address: InetAddress, val prefixSize: Int) extends Comparable[Subnet] {
if (prefixSize < 0) throw new IllegalArgumentException
address match {
case _: Inet4Address => if (prefixSize > 32) throw new IllegalArgumentException
case _: Inet6Address => if (prefixSize > 128) throw new IllegalArgumentException
}
private def addressLength = address.getAddress.length << 3
if (prefixSize < 0 || prefixSize > addressLength) throw new IllegalArgumentException
override def toString: String = if (address match {
case _: Inet4Address => prefixSize == 32
case _: Inet6Address => prefixSize == 128
}) address.getHostAddress else address.getHostAddress + '/' + prefixSize
override def toString: String =
if (prefixSize == addressLength) address.getHostAddress else address.getHostAddress + '/' + prefixSize
override def compareTo(that: Subnet): Int = {
val addrThis = address.getAddress
@@ -31,19 +27,21 @@ class Subnet(val address: InetAddress, val prefixSize: Int) extends Comparable[S
}
prefixSize compare that.prefixSize
}
override def equals(other: Any): Boolean = other match {
case that: Subnet => address == that.address && prefixSize == that.prefixSize
case _ => false
}
override def hashCode: Int = Objects.hash(address, prefixSize: Integer)
}
object Subnet {
@throws[IllegalArgumentException]
def fromString(value: String): Subnet = {
val parts = value.split("/")
if (!Utils.isNumeric(parts(0))) throw new IllegalArgumentException()
val addr = InetAddress.getByName(parts(0))
val addr = Utils.parseNumericAddress(parts(0))
parts.length match {
case 1 => new Subnet(addr, addr match {
case _: Inet4Address => 32
case _: Inet6Address => 128
})
case 1 => new Subnet(addr, addr.getAddress.length << 3)
case 2 => new Subnet(addr, parts(1).toInt)
case _ => throw new IllegalArgumentException()
}
@@ -77,9 +77,6 @@ class DBHelper(val context: Context)
} 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;")
}
if (oldVersion < 11) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
@@ -89,9 +86,9 @@ class DBHelper(val context: Context)
TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," +
" ipv6, individual FROM `tmp`;")
" udpdns, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, ipv6," +
" individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;")
} else if (oldVersion < 13) {
@@ -127,10 +124,10 @@ class DBHelper(val context: Context)
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, proxyApps," +
" bypass, udpdns, auth, ipv6, individual, tx, rx, date, userOrder, plugin) " +
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, " +
"proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, plugin) " +
"SELECT id, name, host, localPort, CASE WHEN kcp = 1 THEN kcpPort ELSE remotePort END, password, method, " +
"route, remoteDns, proxyApps, bypass, udpdns, auth, ipv6, individual, tx, rx, date, userOrder, " +
"route, remoteDns, proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, " +
"CASE WHEN kcp = 1 THEN 'kcptun ' || kcpcli ELSE NULL END FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;")
@@ -20,13 +20,13 @@
package com.github.shadowsocks.database
import java.net.URLEncoder
import java.util.Locale
import android.content.SharedPreferences
import android.net.Uri
import android.os.Binder
import android.text.TextUtils
import android.util.Base64
import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.utils.Key
import com.j256.ormlite.field.{DataType, DatabaseField}
@@ -68,9 +68,6 @@ class Profile {
@DatabaseField
var udpdns: Boolean = _
@DatabaseField
var auth: Boolean = _
@DatabaseField
var ipv6: Boolean = _
@@ -96,11 +93,20 @@ class Profile {
def nameIsEmpty: Boolean = name == null || name.isEmpty
def getName: String = if (nameIsEmpty) formattedAddress else name
override def toString: String = "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) +
(if (TextUtils.isEmpty(name)) "" else '#' + URLEncoder.encode(name, "utf-8"))
def isMethodUnsafe: Boolean = "table".equalsIgnoreCase(method) || "rc4".equalsIgnoreCase(method)
def toUri: Uri = {
val builder = new Uri.Builder()
.scheme("ss")
.encodedAuthority("%s@%s:%d".formatLocal(Locale.ENGLISH,
Base64.encodeToString("%s:%s".formatLocal(Locale.ENGLISH, method, password).getBytes,
Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE),
if (host.contains(':')) '[' + host + ']' else host, remotePort))
val configuration = new PluginConfiguration(plugin)
if (configuration.selected.nonEmpty)
builder.appendQueryParameter(Key.plugin, configuration.selectedOptions.toString(false))
if (!nameIsEmpty) builder.fragment(name)
builder.build()
}
override def toString: String = toUri.toString
def serialize(editor: SharedPreferences.Editor): SharedPreferences.Editor = editor
.putString(Key.name, name)
@@ -114,7 +120,6 @@ class Profile {
.putBoolean(Key.proxyApps, proxyApps)
.putBoolean(Key.bypass, bypass)
.putBoolean(Key.udpdns, udpdns)
.putBoolean(Key.auth, auth)
.putBoolean(Key.ipv6, ipv6)
.putString(Key.individual, individual)
.putString(Key.plugin, plugin)
@@ -132,7 +137,6 @@ class Profile {
proxyApps = pref.getBoolean(Key.proxyApps, false)
bypass = pref.getBoolean(Key.bypass, false)
udpdns = pref.getBoolean(Key.udpdns, false)
auth = pref.getBoolean(Key.auth, false)
ipv6 = pref.getBoolean(Key.ipv6, false)
individual = pref.getString(Key.individual, null)
plugin = pref.getString(Key.plugin, null)
@@ -50,6 +50,20 @@ object PluginManager {
|RSo6EooQ7+NBejOXysqIF1q0BJs8Y5s/CaTOmgbL7uPCkzArB6SS/hzXgDk5gw6v
|wkGeOtzcj1DlbUTvt1s5GlnwBTGUmkbLx+YUje+n+IBgMbohLUDYBtUHylRVgMsc
|1WS67kDqeJiiQZvrxvyW6CZZ/MIGI+uAkkj3DqJpaZirkwPgvpcOIrjZy0uFvQM=
""", Base64.DEFAULT)) +
new Signature(Base64.decode( // @madeye
"""
|MIICQzCCAaygAwIBAgIETV9OhjANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJjbjERMA8GA1UE
|CBMIU2hhbmdoYWkxDzANBgNVBAcTBlB1ZG9uZzEUMBIGA1UEChMLRnVkYW4gVW5pdi4xDDAKBgNV
|BAsTA1BQSTEPMA0GA1UEAxMGTWF4IEx2MB4XDTExMDIxOTA1MDA1NFoXDTM2MDIxMzA1MDA1NFow
|ZjELMAkGA1UEBhMCY24xETAPBgNVBAgTCFNoYW5naGFpMQ8wDQYDVQQHEwZQdWRvbmcxFDASBgNV
|BAoTC0Z1ZGFuIFVuaXYuMQwwCgYDVQQLEwNQUEkxDzANBgNVBAMTBk1heCBMdjCBnzANBgkqhkiG
|9w0BAQEFAAOBjQAwgYkCgYEAq6lA8LqdeEI+es9SDX85aIcx8LoL3cc//iRRi+2mFIWvzvZ+bLKr
|4Wd0rhu/iU7OeMm2GvySFyw/GdMh1bqh5nNPLiRxAlZxpaZxLOdRcxuvh5Nc5yzjM+QBv8ECmuvu
|AOvvT3UDmA0AMQjZqSCmxWIxc/cClZ/0DubreBo2st0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAQ
|Iqonxpwk2ay+Dm5RhFfZyG9SatM/JNFx2OdErU16WzuK1ItotXGVJaxCZv3u/tTwM5aaMACGED5n
|AvHaDGCWynY74oDAopM4liF/yLe1wmZDu6Zo/7fXrH+T03LBgj2fcIkUfN1AA4dvnBo8XWAm9VrI
|1iNuLIssdhDz3IL9Yg==
""", Base64.DEFAULT))
private var receiver: BroadcastReceiver = _
@@ -45,86 +45,6 @@ object ConfigUtils {
" port = %d;\n" +
" type = socks5;\n" +
"}\n"
val PDNSD_LOCAL =
"""
|global {
| perm_cache = 2048;
| %s
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = tcp_only;
| min_ttl = 15m;
| max_ttl = 1w;
| timeout = 10;
| daemon = off;
|}
|
|server {
| label = "local";
| ip = 127.0.0.1;
| port = %d;
| reject = %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
val PDNSD_DIRECT =
"""
|global {
| perm_cache = 2048;
| %s
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = udp_only;
| min_ttl = 15m;
| max_ttl = 1w;
| timeout = 10;
| daemon = off;
| par_queries = 4;
|}
|
|server {
| label = "remote-servers";
| ip = %s;
| timeout = 3;
| query_method = udp_only;
| %s
| policy = included;
| reject = %s;
| reject_policy = fail;
| reject_recursively = on;
|}
|
|server {
| label = "local-server";
| ip = 127.0.0.1;
| query_method = tcp_only;
| port = %d;
| reject = %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 {
@@ -141,7 +61,6 @@ object Key {
val proxyApps = "isProxyApps"
val bypass = "isBypassApps"
val udpdns = "isUdpDns"
val auth = "isAuth"
val ipv6 = "isIpv6"
val host = "proxy"
@@ -20,32 +20,55 @@
package com.github.shadowsocks.utils
import java.net.URLDecoder
import java.net.URI
import android.net.Uri
import android.util.{Base64, Log}
import com.github.shadowsocks.database.Profile
object Parser {
val TAG = "ShadowParser"
private val pattern = "(?i)ss://([A-Za-z0-9+-/=_]+)(#(.+))?".r
private val decodedPattern = "(?i)^((.+?)(-auth)??:(.*)@(.+?):(\\d+?))$".r
private val pattern = "(?i)ss://[-a-zA-Z0-9+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z0-9+&@#/%=~_|\\[\\]]".r
private val userInfoPattern = "^(.+?):(.*)$".r
private val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)$".r
def findAll(data: CharSequence): Iterator[Profile] =
pattern.findAllMatchIn(if (data == null) "" else data).map(m => try
decodedPattern.findFirstMatchIn(new String(Base64.decode(m.group(1), Base64.NO_PADDING), "UTF-8")) match {
case Some(ss) =>
val profile = new Profile
profile.method = ss.group(2).toLowerCase
if (ss.group(3) != null) profile.auth = true
profile.password = ss.group(4)
profile.host = ss.group(5)
profile.remotePort = ss.group(6).toInt
if (m.group(2) != null) profile.name = URLDecoder.decode(m.group(3), "utf-8")
profile
case _ => null
} catch {
case ex: Exception =>
Log.e(TAG, "parser error: " + m.source, ex)// Ignore
null
}).filter(_ != null)
pattern.findAllMatchIn(if (data == null) "" else data).map(m => {
val uri = Uri.parse(m.matched)
uri.getUserInfo match {
case null => val str = new String(Base64.decode(uri.getHost, Base64.NO_PADDING), "UTF-8"); str match {
case legacyPattern(method, password, host, port) => // legacy uri
val profile = new Profile
profile.method = method.toLowerCase
profile.password = password
profile.host = host
profile.remotePort = port.toInt
profile.plugin = uri.getQueryParameter(Key.plugin)
profile.name = uri.getFragment
profile
case _ =>
Log.e(TAG, "Unrecognized URI: " + m.matched)
null
}
case userInfo =>
new String(Base64.decode(userInfo, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE), "UTF-8") match {
case userInfoPattern(method, password) =>
val profile = new Profile
profile.method = method
profile.password = password
// bug in Android: https://code.google.com/p/android/issues/detail?id=192855
val javaURI = new URI(m.matched)
profile.host = javaURI.getHost
if (profile.host.headOption.contains('[') && profile.host.lastOption.contains(']'))
profile.host = profile.host.substring(1, profile.host.length - 1)
profile.remotePort = javaURI.getPort
profile.plugin = uri.getQueryParameter(Key.plugin)
profile.name = uri.getFragment
profile
case _ =>
Log.e(TAG, "Unknown user info: " + m.matched)
null
}
}
}).filter(_ != null)
}
@@ -45,7 +45,7 @@ object TrafficMonitor {
def formatTraffic(size: Long): String = {
var n: Double = size
var i = -1
while (n >= 1000) {
while (n >= 999.5) {
n /= 1024
i = i + 1
}
@@ -20,6 +20,7 @@
package com.github.shadowsocks.utils
import java.lang.reflect.InvocationTargetException
import java.net._
import java.security.MessageDigest
@@ -79,7 +80,9 @@ object Utils {
def resolve(host: String, addrType: Int): Option[String] = {
try {
val lookup = new Lookup(host, addrType)
val resolver = new SimpleResolver("114.114.114.114")
val resolver = new SimpleResolver("208.67.220.220")
resolver.setTCP(true)
resolver.setPort(443)
resolver.setTimeout(5)
lookup.setResolver(resolver)
val result = lookup.run()
@@ -108,7 +111,15 @@ object Utils {
.orElse(resolve(host))
private lazy val isNumericMethod = classOf[InetAddress].getMethod("isNumeric", classOf[String])
private lazy val parseNumericAddressMethod = classOf[InetAddress].getMethod("parseNumericAddress", classOf[String])
def isNumeric(address: String): Boolean = isNumericMethod.invoke(null, address).asInstanceOf[Boolean]
def parseNumericAddress(address: String): InetAddress =
try parseNumericAddressMethod.invoke(null, address).asInstanceOf[InetAddress] catch {
case exc: InvocationTargetException => throw exc.getCause match {
case null => exc
case cause => cause
}
}
/**
* If there exists a valid IPv6 interface
+82
View File
@@ -0,0 +1,82 @@
#!/bin/bash
function try () {
"$@" || exit -1
}
[ -z "$ANDROID_NDK_HOME" ] && ANDROID_NDK_HOME=~/android-ndk-r12b
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
MIN_API=$1
TARGET=$DIR/../main/jni/overture
DEPS=$DIR/.deps
ANDROID_ARM_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-arm
ANDROID_ARM64_TOOLCHAIN=$DEPS/android-toolchain-21-arm64
ANDROID_X86_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-x86
ANDROID_ARM_CC=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-gcc
ANDROID_ARM_STRIP=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-strip
ANDROID_ARM64_CC=$ANDROID_ARM64_TOOLCHAIN/bin/aarch64-linux-android-gcc
ANDROID_ARM64_STRIP=$ANDROID_ARM64_TOOLCHAIN/bin/aarch64-linux-android-strip
ANDROID_X86_CC=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-gcc
ANDROID_X86_STRIP=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-strip
try mkdir -p $DEPS $TARGET/armeabi-v7a $TARGET/x86 $TARGET/arm64-v8a
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 $MIN_API --install-dir $ANDROID_ARM_TOOLCHAIN
fi
if [ ! -d "$ANDROID_ARM64_TOOLCHAIN" ]; then
echo "Make standalone toolchain for ARM64 arch"
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm64 \
--api 21 --install-dir $ANDROID_ARM64_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 $MIN_API --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=$DIR
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
echo "Get dependences for overture"
go get -u github.com/tools/godep
pushd $GOPATH/src/github.com/shadowsocks/overture/main
godep restore
echo "Cross compile overture 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 main
try mv main $TARGET/armeabi-v7a/liboverture.so
echo "Cross compile overture for arm64"
try env CGO_ENABLED=1 CC=$ANDROID_ARM64_CC GOOS=android GOARCH=arm64 go build -ldflags="-s -w"
try $ANDROID_ARM64_STRIP main
try mv main $TARGET/arm64-v8a/liboverture.so
echo "Cross compile overture for x86"
try env CGO_ENABLED=1 CC=$ANDROID_X86_CC GOOS=android GOARCH=386 go build -ldflags="-s -w"
try $ANDROID_X86_STRIP main
try mv main $TARGET/x86/liboverture.so
popd
echo "Successfully build overture"
+6
View File
@@ -0,0 +1,6 @@
* 0.0.3:
* Update support library version to 25.2.0.
* 0.0.2:
* Add `getOrDefault` to `PluginOptions`;
* Update support library version to 25.1.1.
* 0.0.1: Initial release.
+4 -5
View File
@@ -1,5 +1,7 @@
# shadowsocks-android plugin framework
[Documentation](doc.md) | [Change log](CHANGES.md)
Support library for easier development on [shadowsocks
plugin](https://github.com/shadowsocks/shadowsocks-org/issues/28) for Android. Also includes some
useful resources to easily get consistent styling with the main app.
@@ -19,9 +21,6 @@ This library is designed with Java interoperability in mind so theoretically you
library with other languages and/or build tools but there isn't documentation for that yet. This
guide is written for Scala + SBT. Contributions are welcome.
The following is just a simple guide to get you started, for more technical details, see
[doc.md](https://github.com/shadowsocks/shadowsocks-android/blob/develop/plugin/doc.md).
### Package name
There are no arbitrary restrictions/requirements on package name, component name and content
@@ -37,7 +36,7 @@ First you need to add this library to your dependencies. This library is written
and it's most convenient to use it with SBT:
```scala
libraryDependencies += "com.github.shadowsocks" %% "plugin" % "0.0.1-SNAPSHOT"
libraryDependencies += "com.github.shadowsocks" %% "plugin" % "0.0.2"
```
### Native binary configuration
@@ -88,7 +87,7 @@ Then add it to your manifest:
...
<provider android:name=".BinaryProvider"
android:exported="true"
android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID">
android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER">
<intent-filter>
<action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/>
</intent-filter>
+28 -1
View File
@@ -2,4 +2,31 @@ enablePlugins(AndroidLib)
android.useSupportVectors
name := "plugin"
version := "0.0.1"
version := "0.0.3"
pomExtra in Global := {
<url>https://github.com/shadowsocks/shadowsocks-android</url>
<licenses>
<license>
<name>GPLv3</name>
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
</license>
</licenses>
<scm>
<connection>scm:git:git://github.com/shadowsocks/shadowsocks-android.git</connection>
<developerConnection>scm:git:git@github.com:shadowsocks/shadowsocks-android.git</developerConnection>
<url>github.com/shadowsocks/shadowsocks-android</url>
</scm>
<developers>
<developer>
<id>madeye</id>
<name>Max Lv</name>
<url>https://github.com/madeye</url>
</developer>
<developer>
<id>Mygod</id>
<name>Mygod Studio</name>
<url>https://github.com/Mygod</url>
</developer>
</developers>
}
+3 -3
View File
@@ -153,7 +153,7 @@ This corresponds to `com.github.shadowsocks.plugin.NativePluginProvider` in the
...
<provider android:name=".BinaryProvider"
android:exported="true"
android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID">
android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER">
<intent-filter>
<action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/>
</intent-filter>
@@ -196,7 +196,7 @@ This feature hasn't been implemented yet.
Plugins are certified using package signatures and shadowsocks-android will consider these
signatures as trusted:
* Signatures by [trusted sources](https://github.com/shadowsocks/shadowsocks-android/blob/develop/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala#L22)
* Signatures by [trusted sources](/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala#L22)
which includes:
- @madeye, i.e. the signer of the main repo;
- The main repo doesn't contain any other trusted signatures. Third-party forks should add their
@@ -226,5 +226,5 @@ Plugin app must include this in their application tag: (which should be automati
```
<meta-data android:name="com.github.shadowsocks.plugin.version"
android:value="0.0.1"/>
android:value="0.0.2"/>
```
+1 -1
View File
@@ -6,6 +6,6 @@
<application
android:theme="@style/Theme.Shadowsocks">
<meta-data android:name="com.github.shadowsocks.plugin.version"
android:value="0.0.1"/>
android:value="0.0.2"/>
</application>
</manifest>
@@ -24,6 +24,14 @@ public final class PluginOptions extends HashMap<String, String> {
super(initialCapacity, loadFactor);
}
// TODO: this method is not needed since API 24
public String getOrDefault(Object key, String defaultValue) {
String v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
private PluginOptions(String options, boolean parseId) {
this();
if (TextUtils.isEmpty(options)) return;
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- proxy category -->
<string name="proxy_cat">"サーバー設定"</string>
<!-- feature category -->
<string name="feature_cat">"ファンクション設定"</string>
<!-- alert category -->
<string name="yes">"はい"</string>
<string name="no">"いいえ"</string>
<string name="unsaved_changes_prompt">"変更は適応されておりません、保存しますか?"</string>
<string name="apply">"適用"</string>
</resources>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- proxy category -->
<string name="proxy_cat">"서버 설정"</string>
<!-- feature category -->
<string name="feature_cat">"기능 설정"</string>
</resources>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- proxy category -->
<string name="proxy_cat">"Настройки Сервера"</string>
<!-- feature category -->
<string name="feature_cat">"Дополнительные Настройки"</string>
</resources>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- proxy category -->
<string name="proxy_cat">"服务器设置"</string>
<!-- feature category -->
<string name="feature_cat">"功能设置"</string>
<!-- alert category -->
<string name="yes">"是"</string>
<string name="no">"否"</string>
<string name="unsaved_changes_prompt">"保存修改吗?"</string>
<string name="apply">"应用"</string>
</resources>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- proxy category -->
<string name="proxy_cat">"伺服器設定"</string>
<!-- feature category -->
<string name="feature_cat">"功能設定"</string>
<!-- alert category -->
<string name="yes">"是"</string>
<string name="no">"否"</string>
<string name="unsaved_changes_prompt">"要儲存變更嗎?"</string>
<string name="apply">"套用"</string>
</resources>
@@ -15,7 +15,7 @@ import android.os.{Bundle, ParcelFileDescriptor}
* &lt;application&gt;
* ...
* &lt;provider android:name="com.github.shadowsocks.$PLUGIN_ID.BinaryProvider"
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID"&gt;
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider"&gt;
* &lt;intent-filter&gt;
* &lt;category android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" /&gt;
* &lt;/intent-filter&gt;
+3 -1
View File
@@ -1,4 +1,4 @@
addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.3")
addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.7")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
@@ -7,3 +7,5 @@ addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") // fot sbt-0.13.5 or higher
addSbtPlugin("org.scala-android" % "sbt-android-gms" % "0.4")
-23
View File
@@ -1,23 +0,0 @@
sonatypeProfileName := "com.github.shadowsocks"
pomExtra in Global := {
<url>https://github.com/shadowsocks/shadowsocks-android</url>
<licenses>
<license>
<name>GPLv3</name>
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
</license>
</licenses>
<scm>
<connection>scm:git:git://github.com/shadowsocks/shadowsocks-android.git</connection>
<developerConnection>scm:git:git@github.com:shadowsocks/shadowsocks-android.git</developerConnection>
<url>github.com/shadowsocks/shadowsocks-android</url>
</scm>
<developers>
<developer>
<id>madeye</id>
<name>Max Lv</name>
<url>https://github.com/madeye/</url>
</developer>
</developers>
}