Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59f02cfa53 | ||
|
|
bd782dae98 | ||
|
|
ea738d17d7 | ||
|
|
915b1a74b1 | ||
|
|
9898ab88e0 | ||
|
|
c082a23c37 | ||
|
|
6636cf815d | ||
|
|
6df8f14fb2 | ||
|
|
0b88881fa4 | ||
|
|
85532a6b48 | ||
|
|
93274ffb04 | ||
|
|
fcd6ed847a | ||
|
|
36c4d80ee4 | ||
|
|
e864873ee4 | ||
|
|
80411c61d6 | ||
|
|
adb7285d54 | ||
|
|
7356eded16 | ||
|
|
1268376b68 | ||
|
|
b71019149f | ||
|
|
451e9c1fd3 | ||
|
|
9d5c9456c9 | ||
|
|
4988faf574 | ||
|
|
189058b5c4 | ||
|
|
529ee3a929 | ||
|
|
90cc3dbf2d | ||
|
|
6fe38f76be | ||
|
|
4df1d68501 | ||
|
|
9eceefcceb | ||
|
|
af6e58ec46 | ||
|
|
64c8529705 | ||
|
|
b1670679d7 | ||
|
|
5ac1343b5d | ||
|
|
41f4f3a3bc | ||
|
|
c21bd74302 | ||
|
|
a8822330a3 | ||
|
|
1cf6e4802c | ||
|
|
f64014eb7b | ||
|
|
2f6f991e6e | ||
|
|
90e00a3857 | ||
|
|
712b764a9b | ||
|
|
019f9d30e8 | ||
|
|
9c97dc9bf6 | ||
|
|
7d2e0a515f | ||
|
|
26b68876f4 | ||
|
|
a494f56ce8 | ||
|
|
a433822d0a | ||
|
|
60d7d90565 | ||
|
|
d2ba9493c0 | ||
|
|
0eaece7af9 | ||
|
|
96320896aa | ||
|
|
54efe36ffc | ||
|
|
d5d6740a5a | ||
|
|
2e69eafdf8 | ||
|
|
86773085c3 | ||
|
|
b6667b9e0f | ||
|
|
0ee7e9d40e | ||
|
|
5f2ce1804f | ||
|
|
a5e316999a | ||
|
|
64a3d75ba2 | ||
|
|
9d09c9a097 | ||
|
|
b78f48504f | ||
|
|
a61b03c86c | ||
|
|
f43362fb18 | ||
|
|
4f0e527df7 | ||
|
|
a8878298cc | ||
|
|
c920c0daa4 | ||
|
|
8e3854b496 | ||
|
|
ad42167b2e | ||
|
|
3bf0963c5a | ||
|
|
c3315d35ed | ||
|
|
103dc10a0a | ||
|
|
36c94b8d83 | ||
|
|
890be54403 | ||
|
|
31984d1001 | ||
|
|
df72b29302 | ||
|
|
f30c31ae3e | ||
|
|
05963b6c82 | ||
|
|
98da4cd6e5 | ||
|
|
64da35c59b | ||
|
|
458a3473b1 | ||
|
|
66db347215 | ||
|
|
f9e32c995b | ||
|
|
0626ce8787 | ||
|
|
11d827c6ba | ||
|
|
dc46cd971a | ||
|
|
876e92f275 | ||
|
|
41747c976b | ||
|
|
8277b07d24 | ||
|
|
b982867130 | ||
|
|
491710679a | ||
|
|
24c6aca85c | ||
|
|
293e9b5dff | ||
|
|
3ac77e1f8d | ||
|
|
fd2c67d0b8 | ||
|
|
684698c8e4 | ||
|
|
6a02d4b985 | ||
|
|
7219ce429c | ||
|
|
9975b135d0 | ||
|
|
5d46ef45e3 | ||
|
|
018aa781d3 | ||
|
|
f7e2f8694d | ||
|
|
2d86122297 | ||
|
|
ad0578d073 | ||
|
|
b671bc2c50 | ||
|
|
be5036183d | ||
|
|
212c3f666b | ||
|
|
deba1028c0 | ||
|
|
5559295beb | ||
|
|
41eec3e351 | ||
|
|
56709385b1 | ||
|
|
dc13249a1e | ||
|
|
d579660682 | ||
|
|
1e96a13ab1 | ||
|
|
b71b27fbdf | ||
|
|
3fa8a0c90c | ||
|
|
f121ea2549 | ||
|
|
df1e40d60b | ||
|
|
ee08f02660 | ||
|
|
7af26d1a4b | ||
|
|
88319ecac7 | ||
|
|
b397ab2bbd | ||
|
|
33c087f13f | ||
|
|
4996d4fc52 | ||
|
|
14fd86c716 | ||
|
|
3b9a35d3e6 | ||
|
|
3b93302539 | ||
|
|
8de1dc1f7a | ||
|
|
55667c9e76 | ||
|
|
e81c4f257e | ||
|
|
61a926ff5e | ||
|
|
8d1f2da1f0 | ||
|
|
30c8c7e6eb | ||
|
|
dadeffa903 | ||
|
|
caf5731ff6 | ||
|
|
437d8c3026 | ||
|
|
44b47e6cd3 |
+2
-1
@@ -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?
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+3
-2
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
@@ -0,0 +1 @@
|
||||
127.0.0.1 localhost
|
||||
@@ -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="mailto:max.c.lv@gmail.com">max.c.lv@gmail.com</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>
|
||||
|
||||
+30
-7
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Submodule mobile/src/main/jni/libsodium updated: a81cea2366...70170c28c8
Submodule mobile/src/main/jni/pdnsd deleted from f6b0a56c74
Submodule mobile/src/main/jni/shadowsocks-libev updated: 1c2b6170b9...aaff27ed4d
@@ -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.
@@ -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 < 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>
|
||||
@@ -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 < 3.7.1"</string>
|
||||
<string name="udp_dns">"DNS 포워딩"</string>
|
||||
</resources>
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 < 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>
|
||||
@@ -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
|
||||
|
||||
Submodule
+1
Submodule mobile/src/overture/go added at 86036dcce8
Executable
+82
@@ -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"
|
||||
Submodule mobile/src/overture/src/github.com/shadowsocks/overture added at 7263b82d2e
@@ -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
@@ -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
@@ -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
@@ -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"/>
|
||||
```
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
* <application>
|
||||
* ...
|
||||
* <provider android:name="com.github.shadowsocks.$PLUGIN_ID.BinaryProvider"
|
||||
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID">
|
||||
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider">
|
||||
* <intent-filter>
|
||||
* <category android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" />
|
||||
* </intent-filter>
|
||||
|
||||
+3
-1
@@ -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")
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
Reference in New Issue
Block a user