Commit b9828671 authored by Max Lv's avatar Max Lv

Merge branch 'beta'

parents caf5731f 49171067
...@@ -29,7 +29,8 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails ...@@ -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; 1. Broken VPNService implementation, especially for IPv6;
2. Aggressive (or called broken) background service killing policy. 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? ### How to pause Shadowsocks service?
......
...@@ -30,7 +30,7 @@ _Put an `x` inside the [ ] that applies._ ...@@ -30,7 +30,7 @@ _Put an `x` inside the [ ] that applies._
* [ ] Per-App Proxy * [ ] Per-App Proxy
* [ ] Bypass mode * [ ] Bypass mode
* [ ] UDP Forwarding * [ ] UDP Forwarding
* KCP Parameters: (leave blank if not enabled) * Plugin configuration (if applicable):
* [ ] Auto Connect * [ ] Auto Connect
* [ ] TCP Fast Open * [ ] TCP Fast Open
* [ ] NAT mode * [ ] NAT mode
......
classes
bin
gen
target target
local.properties local.properties
.classpath
.project
.settings
tests/bin
tests/gen
tests/local.properties
NUL
gen_*
*.class
*.o
local.sbt local.sbt
.deps
/proguard-sbt.txt /proguard-sbt.txt
#Intellij IDEA #Intellij IDEA
*.iml
*.ipr
*.iws
.idea/ .idea/
out/
.d .d
#NDK mobile/src/main/libs
src/main/obj mobile/src/main/obj
src/main/libs
src/main/assets/armeabi-v7a/*
src/main/assets/x86/*
jni/Application.mk
[submodule "src/main/jni/shadowsocks-libev"] [submodule "mobile/src/main/jni/shadowsocks-libev"]
path = src/main/jni/shadowsocks-libev path = mobile/src/main/jni/shadowsocks-libev
url = https://github.com/shadowsocks/shadowsocks-libev url = https://github.com/shadowsocks/shadowsocks-libev
[submodule "src/main/jni/badvpn"] [submodule "mobile/src/main/jni/badvpn"]
path = src/main/jni/badvpn path = mobile/src/main/jni/badvpn
url = https://github.com/shadowsocks/badvpn.git url = https://github.com/shadowsocks/badvpn.git
branch = shadowsocks-android branch = shadowsocks-android
[submodule "src/main/jni/libancillary"] [submodule "mobile/src/main/jni/libancillary"]
path = src/main/jni/libancillary path = mobile/src/main/jni/libancillary
url = https://github.com/shadowsocks/libancillary.git url = https://github.com/shadowsocks/libancillary.git
branch = shadowsocks-android branch = shadowsocks-android
[submodule "src/main/jni/libevent"] [submodule "mobile/src/main/jni/libevent"]
path = src/main/jni/libevent path = mobile/src/main/jni/libevent
url = https://github.com/shadowsocks/libevent.git url = https://github.com/shadowsocks/libevent.git
branch = shadowsocks-android branch = shadowsocks-android
[submodule "src/main/jni/redsocks"] [submodule "mobile/src/main/jni/redsocks"]
path = src/main/jni/redsocks path = mobile/src/main/jni/redsocks
url = https://github.com/shadowsocks/redsocks.git url = https://github.com/shadowsocks/redsocks.git
branch = shadowsocks-android branch = shadowsocks-android
[submodule "src/main/jni/pdnsd"] [submodule "mobile/src/main/jni/pdnsd"]
path = src/main/jni/pdnsd path = mobile/src/main/jni/pdnsd
url = https://github.com/shadowsocks/pdnsd.git url = https://github.com/shadowsocks/pdnsd.git
branch = shadowsocks-android branch = shadowsocks-android
[submodule "kcptun/kcptun"] [submodule "mobile/src/main/jni/mbedtls"]
path = kcptun/kcptun path = mobile/src/main/jni/mbedtls
url = https://github.com/shadowsocks/kcptun
[submodule "kcptun/go"]
path = kcptun/go
url = https://github.com/shadowsocks/go
[submodule "src/main/jni/mbedtls"]
path = src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls url = https://github.com/ARMmbed/mbedtls
[submodule "src/main/jni/pcre"] [submodule "mobile/src/main/jni/pcre"]
path = src/main/jni/pcre path = mobile/src/main/jni/pcre
url = https://android.googlesource.com/platform/external/pcre url = https://android.googlesource.com/platform/external/pcre
[submodule "mobile/src/main/jni/libsodium"]
path = mobile/src/main/jni/libsodium
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
...@@ -7,7 +7,6 @@ env: ...@@ -7,7 +7,6 @@ env:
global: global:
- NDK_VERSION=r12b - NDK_VERSION=r12b
- NDK_CCACHE=ccache - NDK_CCACHE=ccache
- GOROOT_BOOTSTRAP=$GOROOT
- ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION} - ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION}
- SBTPATH=$HOME/.sbt - SBTPATH=$HOME/.sbt
- PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH} - PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
...@@ -50,4 +49,4 @@ install: ...@@ -50,4 +49,4 @@ install:
fi fi
script: script:
- $SBTPATH/sbt -DNPROC=$(nproc) native-build android:package-debug - $SBTPATH/sbt android:package
...@@ -12,7 +12,6 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala. ...@@ -12,7 +12,6 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* JDK 1.8 * JDK 1.8
* SBT 0.13.0+ * SBT 0.13.0+
* Go 1.4+
* Android SDK * Android SDK
- Build Tools 25+ - Build Tools 25+
- Android Support Repository and Google Repository (see `build.sbt` for version) - Android Support Repository and Google Repository (see `build.sbt` for version)
...@@ -22,16 +21,15 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala. ...@@ -22,16 +21,15 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk` * Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
* Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk` * Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk`
* Set environment variable `GOROOT_BOOTSTRAP` to `/path/to/go`
* Create your key following the instructions at https://developer.android.com/studio/publish/app-signing.html * Create your key following the instructions at https://developer.android.com/studio/publish/app-signing.html
* Create `local.properties` from `local.properties.example` with your own key information * Create `mobile/local.properties` from `mobile/local.properties.example` with your own key information
* Invoke the building like this * Invoke the building like this
```bash ```bash
git submodule update --init git submodule update --init --recursive
# Build the App # Build the App
sbt native-build clean android:package-release sbt clean android:package-release
``` ```
### TRANSLATE ### TRANSLATE
...@@ -54,8 +52,8 @@ Translators can go to [POEditor](https://poeditor.com/join/project/u5VHO9vhSf) t ...@@ -54,8 +52,8 @@ Translators can go to [POEditor](https://poeditor.com/join/project/u5VHO9vhSf) t
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</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>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li> <li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li>
<li>kcptun: <a href="https://github.com/xtaci/kcptun/commits/master/LICENSE.md">MIT</a></li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</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>
</ul> </ul>
### LICENSE ### LICENSE
......
scalaVersion := "2.11.8" lazy val commonSettings = Seq(
dexMaxHeap := "4g" scalaVersion := "2.11.8",
dexMaxHeap := "4g",
enablePlugins(AndroidApp)
android.useSupportVectors organization := "com.github.shadowsocks",
name := "shadowsocks" platformTarget := "android-25",
version := "3.4.2"
versionCode := Some(174) compileOrder := CompileOrder.JavaThenScala,
javacOptions ++= "-source" :: "1.7" :: "-target" :: "1.7" :: Nil,
// Speed up ndk-build by reading nproc from environment scalacOptions ++= "-target:jvm-1.7" :: "-Xexperimental" :: Nil,
// and default to 8 ndkArgs := "-j" :: java.lang.Runtime.getRuntime.availableProcessors.toString :: Nil,
val nproc = sys.props.getOrElse("NPROC", default = "8")
ndkArgs := Seq(s"-j$nproc") proguardVersion := "5.3.2",
proguardCache := Seq(),
platformTarget := "android-25"
shrinkResources := true,
compileOrder := CompileOrder.JavaThenScala typedResources := false
javacOptions ++= "-source" :: "1.7" :: "-target" :: "1.7" :: Nil )
scalacOptions ++= "-target:jvm-1.7" :: "-Xexperimental" :: Nil
val supportLibsVersion = "25.1.1"
proguardVersion := "5.3.2" lazy val root = Project(id = "shadowsocks-android", base = file("."))
proguardCache := Seq() .settings(commonSettings)
proguardOptions ++= .aggregate(plugin, mobile)
"-keep class com.github.shadowsocks.System { *; }" ::
"-dontwarn com.google.android.gms.internal.**" :: install in Android := (install in (mobile, Android)).value
"-dontwarn com.j256.ormlite.**" :: run in Android := (run in (mobile, Android)).evaluated
"-dontwarn okio.**" ::
"-dontwarn org.xbill.**" :: lazy val plugin = project
Nil .settings(commonSettings)
.settings(
shrinkResources := true libraryDependencies += "com.android.support" % "preference-v14" % supportLibsVersion
typedResources := false )
resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
lazy val mobile = project
val supportLibsVersion = "25.1.0" .settings(commonSettings)
val playServicesVersion = "10.0.1" .settings(
resolvers += Resolver.jcenterRepo libraryDependencies ++=
libraryDependencies ++= "com.android.support" % "cardview-v7" % supportLibsVersion ::
"com.android.support" % "cardview-v7" % supportLibsVersion :: "com.android.support" % "customtabs" % supportLibsVersion ::
"com.android.support" % "customtabs" % supportLibsVersion :: "com.android.support" % "design" % supportLibsVersion ::
"com.android.support" % "design" % supportLibsVersion :: "com.android.support" % "gridlayout-v7" % supportLibsVersion ::
"com.android.support" % "gridlayout-v7" % supportLibsVersion :: Nil
"com.android.support" % "preference-v14" % supportLibsVersion :: )
"com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" :: .dependsOn(plugin)
"com.evernote" % "android-job" % "1.1.4" ::
"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.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.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
"dnsjava" % "dnsjava" % "2.1.7" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
"net.glxn.qrgen" % "android" % "2.0" ::
Nil
lazy val nativeBuild = TaskKey[Unit]("native-build", "Build native executables")
nativeBuild := {
val logger = streams.value.log
Process("./build.sh") ! logger match {
case 0 => // Success!
case n => sys.error(s"Native build script exit code: $n")
}
}
#!/bin/bash
function try () {
"$@" || exit -1
}
pushd src/main
# Clean up old binaries (no longer used)
rm -rf assets/armeabi-v7a assets/x86 libs/armeabi-v7a libs/x86
mkdir -p libs/armeabi-v7a libs/x86
popd
# Build kcptun
pushd kcptun
try ./make.bash
popd
Subproject commit c3f75e654ddb95bd53351294337649c08e9f9409
Subproject commit c532a50e1aeeaa9dee2428b374c3651a5c95258a
#!/bin/bash
function try () {
"$@" || exit -1
}
[ -z "$ANDROID_NDK_HOME" ] && ANDROID_NDK_HOME=~/android-ndk-r12b
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DEPS=$DIR/.deps
ANDROID_ARM_TOOLCHAIN=$DEPS/android-toolchain-16-arm
ANDROID_X86_TOOLCHAIN=$DEPS/android-toolchain-16-x86
ANDROID_ARM_CC=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-gcc
ANDROID_ARM_STRIP=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-strip
ANDROID_X86_CC=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-gcc
ANDROID_X86_STRIP=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-strip
if [ ! -d "$DEPS" ]; then
mkdir -p $DEPS
fi
if [ ! -d "$ANDROID_ARM_TOOLCHAIN" ]; then
echo "Make standalone toolchain for ARM arch"
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm \
--api 16 --install-dir $ANDROID_ARM_TOOLCHAIN
fi
if [ ! -d "$ANDROID_X86_TOOLCHAIN" ]; then
echo "Make standalone toolchain for X86 arch"
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch x86 \
--api 16 --install-dir $ANDROID_X86_TOOLCHAIN
fi
if [ ! -d "$DIR/go/bin" ]; then
echo "Build the custom go"
pushd $DIR/go/src
try ./make.bash
popd
fi
export GOROOT=$DIR/go
export GOPATH=$DEPS/gopath
export GOBIN=$GOPATH/bin
mkdir -p $GOBIN
export PATH=$GOROOT/bin:$PATH
pushd kcptun/client
echo "Get dependences for kcptun"
go get -u github.com/xtaci/kcp-go
go get -u github.com/xtaci/smux
go get
echo "Cross compile kcptun for arm"
try env CGO_ENABLED=1 CC=$ANDROID_ARM_CC GOOS=android GOARCH=arm GOARM=7 go build -ldflags="-s -w"
try $ANDROID_ARM_STRIP client
try mv client $DIR/../src/main/libs/armeabi-v7a/libkcptun.so
echo "Cross compile kcptun for x86"
try env CGO_ENABLED=1 CC=$ANDROID_X86_CC GOOS=android GOARCH=386 go build -ldflags="-s -w"
try $ANDROID_X86_STRIP client
try mv client $DIR/../src/main/libs/x86/libkcptun.so
popd
echo "Successfully build kcptun"
enablePlugins(AndroidApp)
android.useSupportVectors
name := "shadowsocks"
version := "4.0.1"
versionCode := Some(176)
proguardOptions ++=
"-keep class com.github.shadowsocks.JniHelper { *; }" ::
"-dontwarn com.google.android.gms.internal.**" ::
"-dontwarn com.j256.ormlite.**" ::
"-dontwarn okio.**" ::
"-dontwarn org.xbill.**" ::
Nil
resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
val playServicesVersion = "10.0.1"
resolvers += Resolver.jcenterRepo
libraryDependencies ++=
"com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" ::
"com.evernote" % "android-job" % "1.1.5" ::
"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.j256.ormlite" % "ormlite-android" % "5.0" ::
"com.mikepenz" % "crossfader" % "1.5.0" ::
"com.mikepenz" % "fastadapter" % "2.1.6" ::
"com.mikepenz" % "iconics-core" % "2.8.2" ::
"com.mikepenz" % "materialdrawer" % "5.8.1" ::
"com.mikepenz" % "materialize" % "1.0.0" ::
"com.squareup.okhttp3" % "okhttp" % "3.6.0" ::
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
"dnsjava" % "dnsjava" % "2.1.8" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
"net.glxn.qrgen" % "android" % "2.0" ::
Nil
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks"> xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />
...@@ -18,14 +17,10 @@ ...@@ -18,14 +17,10 @@
android:targetSdkVersion="25"/> android:targetSdkVersion="25"/>
<application <application
android:allowBackup="true"
android:name=".ShadowsocksApplication" android:name=".ShadowsocksApplication"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:backupAgent=".ShadowsocksBackupAgent" android:backupAgent=".ShadowsocksBackupAgent"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.Material"
android:banner="@drawable/ic_start_connected"> android:banner="@drawable/ic_start_connected">
<meta-data android:name="com.google.android.gms.version" <meta-data android:name="com.google.android.gms.version"
......
...@@ -298,8 +298,8 @@ along with this program. If not, see <a href="http://www.gnu.org/licenses/">http ...@@ -298,8 +298,8 @@ along with this program. If not, see <a href="http://www.gnu.org/licenses/">http
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</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>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li> <li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li>
<li>kcptun: <a href="https://github.com/xtaci/kcptun/commits/master/LICENSE.md">MIT</a></li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</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>
</ul> </ul>
</body> </body>
......
...@@ -38,12 +38,23 @@ ...@@ -38,12 +38,23 @@
package com.github.shadowsocks; package com.github.shadowsocks;
public class System { import android.os.Build;
static { import android.system.ErrnoException;
java.lang.System.loadLibrary("system");
}
public static native int exec(String cmd); public class JniHelper {
public static native int sendfd(int fd, String path); static {
public static native void jniclose(int fd); 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);
}
private static native int sigterm(Process process);
public static native int sendFd(int fd, String path);
public static native void close(int fd);
} }
package com.github.shadowsocks.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;
/**
* Commandline objects help handling command lines specifying processes to
* execute.
*
* The class can be used to define a command line as nested elements or as a
* helper to define a command line by an application.
* <p>
* <code>
* &lt;someelement&gt;<br>
* &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
* &nbsp;&nbsp;&lt;/acommandline&gt;<br>
* &lt;/someelement&gt;<br>
* </code>
*
* Based on: https://github.com/apache/ant/blob/588ce1f/src/main/org/apache/tools/ant/types/Commandline.java
*
* Adds support for escape character '\'.
*
* @author Mygod
*/
public final class Commandline {
private Commandline() { }
/**
* Quote the parts of the given array in way that makes them
* usable as command line arguments.
* @param args the list of arguments to quote.
* @return empty string for null or no command, else every argument split
* by spaces and quoted by quoting rules.
*/
public static String toString(Iterable<String> args) {
// empty path return empty string
if (args == null) {
return "";
}
// path containing one or more elements
final StringBuilder result = new StringBuilder();
for (String arg : args) {
if (result.length() > 0) result.append(' ');
for (int j = 0; j < arg.length(); ++j) {
char ch = arg.charAt(j);
switch (ch) {
case ' ': case '\\': case '"': case '\'': result.append('\\'); // intentionally no break
default: result.append(ch);
}
}
}
return result.toString();
}
/**
* Quote the parts of the given array in way that makes them
* usable as command line arguments.
* @param args the list of arguments to quote.
* @return empty string for null or no command, else every argument split
* by spaces and quoted by quoting rules.
*/
public static String toString(String[] args) {
return toString(Arrays.asList(args)); // thanks to Java, arrays aren't iterable
}
/**
* Crack a command line.
* @param toProcess the command line to process.
* @return the command line broken into strings.
* An empty or null toProcess parameter results in a zero sized array.
*/
public static String[] translateCommandline(String toProcess) {
if (toProcess == null || toProcess.length() == 0) {
//no command? no string
return new String[0];
}
// parse with a simple finite state machine
final int normal = 0;
final int inQuote = 1;
final int inDoubleQuote = 2;
int state = normal;
final StringTokenizer tok = new StringTokenizer(toProcess, "\\\"\' ", true);
final ArrayList<String> result = new ArrayList<>();
final StringBuilder current = new StringBuilder();
boolean lastTokenHasBeenQuoted = false;
boolean lastTokenIsSlash = false;
while (tok.hasMoreTokens()) {
String nextTok = tok.nextToken();
switch (state) {
case inQuote:
if ("\'".equals(nextTok)) {
lastTokenHasBeenQuoted = true;
state = normal;
} else {
current.append(nextTok);
}
break;
case inDoubleQuote:
if ("\"".equals(nextTok)) {
if (lastTokenIsSlash) {
current.append(nextTok);
lastTokenIsSlash = false;
} else {
lastTokenHasBeenQuoted = true;
state = normal;
}
} else if ("\\".equals(nextTok)) {
if (lastTokenIsSlash) {
current.append(nextTok);
lastTokenIsSlash = false;
} else lastTokenIsSlash = true;
} else {
if (lastTokenIsSlash) {
current.append("\\"); // unescaped
lastTokenIsSlash = false;
}
current.append(nextTok);
}
break;
default:
if (lastTokenIsSlash) {
current.append(nextTok);
lastTokenIsSlash = false;
} else if ("\\".equals(nextTok)) lastTokenIsSlash = true; else if ("\'".equals(nextTok)) {
state = inQuote;
} else if ("\"".equals(nextTok)) {
state = inDoubleQuote;
} else if (" ".equals(nextTok)) {
if (lastTokenHasBeenQuoted || current.length() != 0) {
result.add(current.toString());
current.setLength(0);
}
} else {
current.append(nextTok);
}
lastTokenHasBeenQuoted = false;
break;
}
}
if (lastTokenHasBeenQuoted || current.length() != 0) {
result.add(current.toString());
}
if (state == inQuote || state == inDoubleQuote) {
throw new IllegalArgumentException("unbalanced quotes in " + toProcess);
}
if (lastTokenIsSlash) throw new IllegalArgumentException("escape character following nothing in " + toProcess);
return result.toArray(new String[result.size()]);
}
}
APP_ABI := armeabi-v7a x86 APP_ABI := armeabi-v7a x86
APP_PLATFORM := android-16 APP_PLATFORM := android-19
APP_STL := stlport_static APP_STL := stlport_static
NDK_TOOLCHAIN_VERSION := clang NDK_TOOLCHAIN_VERSION := clang
File moved
#define LOG_TAG "Shadowsocks" #define LOG_TAG "JniHelper"
#include "jni.h" #include "jni.h"
#include <android/log.h> #include <android/log.h>
#include <sys/system_properties.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <sys/un.h> #include <sys/un.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -16,35 +19,33 @@ ...@@ -16,35 +19,33 @@
#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0) #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 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 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)
jint Java_com_github_shadowsocks_system_exec(JNIEnv *env, jobject thiz, jstring cmd) { static jclass ProcessImpl;
const char *cmd_str = env->GetStringUTFChars(cmd, 0); static jfieldID ProcessImpl_pid;
pid_t pid; static int sdk_version() {
char version[PROP_VALUE_MAX + 1];
__system_property_get("ro.build.version.sdk", version);
return atoi(version);
}
/* Fork off the parent process */ jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jobject process) {
pid = fork(); if (!env->IsInstanceOf(process, ProcessImpl)) {
if (pid < 0) { THROW(env, "java/lang/ClassCastException",
env->ReleaseStringUTFChars(cmd, cmd_str); "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
return -1; return -1;
} }
jint pid = env->GetIntField(process, ProcessImpl_pid);
if (pid > 0) { // Suppress "No such process" errors. We just want the process killed. It's fine if it's already killed.
env->ReleaseStringUTFChars(cmd, cmd_str); return kill(pid, SIGTERM) == -1 && errno != ESRCH ? errno : 0;
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) { void Java_com_github_shadowsocks_jnihelper_close(JNIEnv *env, jobject thiz, jint fd) {
close(fd); close(fd);
} }
jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) { jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd; int fd;
struct sockaddr_un addr; struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0); const char *sock_str = env->GetStringUTFChars(path, 0);
...@@ -75,15 +76,15 @@ jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint t ...@@ -75,15 +76,15 @@ jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint t
return 0; return 0;
} }
static const char *classPathName = "com/github/shadowsocks/System"; static const char *classPathName = "com/github/shadowsocks/JniHelper";
static JNINativeMethod method_table[] = { static JNINativeMethod method_table[] = {
{ "jniclose", "(I)V", { "close", "(I)V",
(void*) Java_com_github_shadowsocks_system_jniclose }, (void*) Java_com_github_shadowsocks_jnihelper_close },
{ "sendfd", "(ILjava/lang/String;)I", { "sendFd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_system_sendfd }, (void*) Java_com_github_shadowsocks_jnihelper_sendfd },
{ "exec", "(Ljava/lang/String;)I", { "sigterm", "(Ljava/lang/Process;)I",
(void*) Java_com_github_shadowsocks_system_exec } (void*) Java_com_github_shadowsocks_jnihelper_sigterm }
}; };
...@@ -139,20 +140,30 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { ...@@ -139,20 +140,30 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
jint result = -1; jint result = -1;
JNIEnv* env = NULL; JNIEnv* env = NULL;
LOGI("JNI_OnLoad"); if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
THROW(env, "java/lang/RuntimeException", "GetEnv failed");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
goto bail; goto bail;
} }
env = uenv.env; env = uenv.env;
if (registerNatives(env) != JNI_TRUE) { if (registerNatives(env) != JNI_TRUE) {
LOGE("ERROR: registerNatives failed"); THROW(env, "java/lang/RuntimeException", "registerNativeMethods failed");
goto bail; goto bail;
} }
result = JNI_VERSION_1_4; 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;
}
}
result = JNI_VERSION_1_6;
bail: bail:
return result; return result;
......
File moved
Subproject commit 981fb847e1e306bc40460de4a810f63f9bbc7212
File moved
Subproject commit e160de24fd146ab84b4eb397c7cffbf34775aa02
Subproject commit 53a1abd2b6fd7e3bf99ce6e2e267c636618d57ef
File moved
pcre @ 222bbf4b
File moved
File moved
File moved
Subproject commit f4b1641636942bc44c96160afad9f023fb6dbf0e
...@@ -10,6 +10,5 @@ ...@@ -10,6 +10,5 @@
<EditText android:id="@+id/content" <EditText android:id="@+id/content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textNoSuggestions" android:inputType="textNoSuggestions|textMultiLine"/>
android:lines="1"/>
</LinearLayout> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:paddingStart="12dp"
android:paddingEnd="12dp">
<ImageView android:id="@android:id/icon"
android:layout_width="52dp"
android:layout_height="52dp"/>
<Space android:layout_width="4dp"
android:layout_height="wrap_content"/>
<LinearLayout android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical">
<TextView android:id="@android:id/text1"
android:textAppearance="?android:attr/textAppearance"
android:textColor="@android:color/primary_text_light"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:ellipsize="end"/>
<TextView android:id="@android:id/text2"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:ellipsize="end"/>
</LinearLayout>
</LinearLayout>
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:id="@+id/container"> android:id="@+id/container">
<TextView android:id="@+id/title" <TextView android:id="@android:id/text1"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
...@@ -54,27 +54,29 @@ ...@@ -54,27 +54,29 @@
android:contentDescription="@string/edit" android:contentDescription="@string/edit"
android:focusable="true"/> android:focusable="true"/>
</LinearLayout> </LinearLayout>
<LinearLayout android:layout_width="match_parent" <RelativeLayout android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp">
android:id="@+id/details"> <TextView android:id="@android:id/text2"
<TextView android:id="@+id/address" android:layout_width="wrap_content"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_alignParentStart="true"
android:layout_weight="1" android:layout_toStartOf="@+id/traffic"
android:maxLines="2" android:layout_marginEnd="8dp"
android:ellipsize="end" android:maxLines="2"
android:textAppearance="?android:attr/textAppearanceSmall" android:ellipsize="end"
android:textColor="?android:attr/textColorSecondary"/> android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/>
<TextView android:id="@+id/traffic" <TextView android:id="@+id/traffic"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:layout_alignParentEnd="true"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/> android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>
</com.github.shadowsocks.widget.BoundedCardView> </com.github.shadowsocks.widget.BoundedCardView>
</FrameLayout> </FrameLayout>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment