Compare commits

..
38 Commits
Author SHA1 Message Date
Max Lv 66d70f1545 update shadowsocks-libev 2015-10-05 16:56:52 +08:00
Max Lv ff0ddd5979 force VpnService as a foreground service 2015-10-05 16:53:22 +08:00
Max Lv bf3933bf2e Merge pull request #379 from Mygod/master
SAM (Scala 2.11.7)
2015-10-04 17:45:02 +08:00
Mygod d111bc8282 Using SAM 2015-10-04 15:13:16 +08:00
Mygod a57c06e304 Use SAM 2015-10-04 13:53:25 +08:00
Mygod 3605e18db2 Update dependencies 2015-10-04 13:43:08 +08:00
Max Lv 1ad8297278 fix #375 2015-10-03 21:06:16 +08:00
Max Lv 458f49d345 Merge pull request #376 from Mygod/master
Migrate to Android 6.0 (1)
2015-10-03 20:53:57 +08:00
Mygod 6b9b7b6749 Migrate to Android 6.0 (1) 2015-10-03 18:37:48 +08:00
Max Lv d542bafb7d bump version 2015-09-28 21:07:39 +08:00
Max Lv bbb62be458 fix a race condition 2015-09-28 21:05:41 +08:00
Max Lv b272d7ab71 fix #368 2015-09-28 13:07:47 +08:00
Max Lv 09d53033cb update shadowsocks-libev 2015-09-28 10:41:44 +08:00
Max Lv 496c809186 update sdk 2015-09-25 12:32:56 +08:00
Max Lv b58ffb7a95 refine strings 2015-09-25 12:06:10 +08:00
Max Lv 3512542043 fix #366 2015-09-25 09:43:09 +08:00
Max Lv 0c940c8860 update shadowsocks-libev 2015-09-24 18:51:45 +08:00
Max Lv 00253ea3f6 update shadowsocks-libev 2015-09-24 13:08:08 +08:00
Max Lv d836aafe40 refine strings 2015-09-23 16:13:39 +08:00
Max Lv 60360e6a19 update shadowsocks-libev 2015-09-23 13:30:56 +08:00
Max Lv 5939f2776c fix a buffer corruption 2015-09-22 20:36:56 +08:00
Max Lv 0cecc62035 update shadowsocks-libev 2015-09-22 18:34:44 +08:00
Max Lv 7a6fbaca15 bump version 2015-09-21 19:09:16 +08:00
Max Lv 2a3ffd768c update shadowsocks-libev 2015-09-21 14:03:02 +08:00
Max Lv 33b775ea5e update plugin 2015-09-18 16:58:03 +08:00
Max Lv 0be4d39606 update shadowsocks-libev 2015-09-18 12:05:20 +08:00
Max Lv ee7e4c9711 update shadowsocks-libev 2015-09-13 14:30:32 +08:00
Max Lv c1c8ff309c update shadowsocks-libev 2015-09-09 09:12:33 +08:00
Max Lv 8724bf325f update shadowsocks-libev 2015-09-05 19:17:28 +08:00
Max Lv 20395ede5f update shadowsocks-libev 2015-09-04 07:13:59 +08:00
Max Lv 336fa3b775 fix a UI bug 2015-08-31 13:05:45 +08:00
Max Lv 11bfef423a update shadowsocks-libev 2015-08-30 12:09:37 +08:00
Max Lv 6e0534b00f bump version 2015-08-20 15:29:04 +08:00
Max Lv bf960f1a64 fix #350 2015-08-20 15:28:36 +08:00
Max Lv b1e01d2158 fix a typo 2015-08-20 13:08:20 +08:00
Max Lv f344eb783c update chnroute 2015-08-20 13:06:52 +08:00
Max Lv 5f6effba09 Merge pull request #346 from clowwindy/nodelay
add tcp_nagle_disable in tun2socks
2015-08-18 17:34:47 +08:00
clowwindy 97cfd1f869 add tcp_nagle_disable in tun2socks 2015-08-18 17:27:49 +08:00
30 changed files with 860 additions and 528 deletions
+17 -17
View File
@@ -1,15 +1,19 @@
import android.Keys._ import android.Keys._
import android.Dependencies.{apklib,aar}
android.Plugin.androidBuild android.Plugin.androidBuild
platformTarget in Android := "android-21" platformTarget in Android := "android-23"
name := "shadowsocks" name := "shadowsocks"
scalaVersion := "2.11.7"
compileOrder in Compile := CompileOrder.JavaThenScala compileOrder in Compile := CompileOrder.JavaThenScala
javacOptions ++= Seq("-source", "1.6", "-target", "1.6")
scalacOptions ++= Seq("-target:jvm-1.6", "-Xexperimental")
ndkJavah in Android := List() ndkJavah in Android := List()
ndkBuild in Android := List() ndkBuild in Android := List()
@@ -22,25 +26,20 @@ resolvers += "JRAF" at "http://JRAF.org/static/maven/2"
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"dnsjava" % "dnsjava" % "2.1.7", "dnsjava" % "dnsjava" % "2.1.7",
"com.github.kevinsawicki" % "http-request" % "5.6", "com.github.kevinsawicki" % "http-request" % "6.0",
"commons-net" % "commons-net" % "3.3", "commons-net" % "commons-net" % "3.3",
"com.google.zxing" % "android-integration" % "3.1.0" "com.google.zxing" % "android-integration" % "3.1.0",
) "com.joanzapata.android" % "android-iconify" % "1.0.11",
libraryDependencies ++= Seq(
"com.joanzapata.android" % "android-iconify" % "1.0.9",
"net.glxn.qrgen" % "android" % "2.0", "net.glxn.qrgen" % "android" % "2.0",
"net.simonvt.menudrawer" % "menudrawer" % "3.0.6", "net.simonvt.menudrawer" % "menudrawer" % "3.0.6",
"com.google.android.gms" % "play-services-base" % "6.5.87", "com.google.android.gms" % "play-services-base" % "8.1.0",
"com.google.android.gms" % "play-services-ads" % "6.5.87", "com.google.android.gms" % "play-services-ads" % "8.1.0",
"com.android.support" % "support-v4" % "21.0.3" "com.google.android.gms" % "play-services-analytics" % "8.1.0",
) "com.android.support" % "support-v4" % "23.0.1",
libraryDependencies ++= Seq(
"com.github.mrengineer13" % "snackbar" % "0.5.0", "com.github.mrengineer13" % "snackbar" % "0.5.0",
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.8.4", "com.nostra13.universalimageloader" % "universal-image-loader" % "1.8.4",
"com.j256.ormlite" % "ormlite-core" % "4.47", "com.j256.ormlite" % "ormlite-core" % "4.48",
"com.j256.ormlite" % "ormlite-android" % "4.47" "com.j256.ormlite" % "ormlite-android" % "4.48"
) )
proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }", proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }",
@@ -52,5 +51,6 @@ proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }
"-keep class * extends com.j256.ormlite.** { *; }", "-keep class * extends com.j256.ormlite.** { *; }",
"-keep class com.joanzapata.** { *; }", "-keep class com.joanzapata.** { *; }",
"-keepattributes *Annotation*", "-keepattributes *Annotation*",
"-dontwarn com.google.android.gms.internal.zzhu",
"-dontwarn org.xbill.**", "-dontwarn org.xbill.**",
"-dontwarn com.actionbarsherlock.**") "-dontwarn com.actionbarsherlock.**")
+13 -11
View File
@@ -10,14 +10,14 @@ import IPy
def main(): def main():
china_list_set = IPy.IPSet() china_list_set = IPy.IPSet()
# for line in sys.stdin: for line in sys.stdin:
# line_params = line.split("|") line_params = line.split("|")
# if len(line_params) < 5 or line_params[2] != "ipv4" or line_params[1] != "CN": if len(line_params) < 5 or line_params[2] != "ipv4" or line_params[1] != "CN":
# continue continue
# ip_addr = line_params[3] ip_addr = line_params[3]
# ip_length = float(line_params[4]) ip_length = float(line_params[4])
# ip_mask = 32 - int(math.ceil(math.log(ip_length, 2))) ip_mask = 32 - int(math.ceil(math.log(ip_length, 2)))
# china_list_set.add(IPy.IP("%s/%d" % (ip_addr, ip_mask))) china_list_set.add(IPy.IP("%s/%d" % (ip_addr, ip_mask)))
# 添加内网地址 # 添加内网地址
internal_list = IPy.IPSet(map(IPy.IP, [ internal_list = IPy.IPSet(map(IPy.IP, [
@@ -41,11 +41,13 @@ def main():
])) ]))
china_list_set += internal_list china_list_set += internal_list
all = china_list_set
# 取反 # 取反
all = IPy.IPSet([IPy.IP("0.0.0.0/0")]) # all = IPy.IPSet([IPy.IP("0.0.0.0/0")])
# 剔除所有孤立的C段 # 剔除所有孤立的C段
for ip in china_list_set: # for ip in china_list_set:
all.discard(ip) # all.discard(ip)
# filter = itertools.ifilter(lambda x: len(x) <= 65536, all) # filter = itertools.ifilter(lambda x: len(x) <= 65536, all)
# for ip in filter: # for ip in filter:
+1 -1
View File
@@ -1,6 +1,6 @@
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns) resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.4.8") addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.1")
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/" resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
+3 -3
View File
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks" package="com.github.shadowsocks"
android:versionCode="115" android:versionCode="118"
android:versionName="2.7.7"> android:versionName="2.8.1">
<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.ACCESS_NETWORK_STATE"/>
@@ -14,7 +14,7 @@
<uses-sdk <uses-sdk
android:minSdkVersion="16" android:minSdkVersion="16"
android:targetSdkVersion="21"/> android:targetSdkVersion="23"/>
<application <application
android:allowBackup="true" android:allowBackup="true"
@@ -10,6 +10,8 @@ public class Config implements Parcelable {
public boolean isBypassApps = false; public boolean isBypassApps = false;
public boolean isTrafficStat = false; public boolean isTrafficStat = false;
public boolean isUdpDns = false; public boolean isUdpDns = false;
public boolean isAuth = false;
public boolean isIpv6 = false;
public String profileName = "Untitled"; public String profileName = "Untitled";
public String proxy = "127.0.0.1"; public String proxy = "127.0.0.1";
@@ -33,13 +35,15 @@ public class Config implements Parcelable {
}; };
public Config(boolean isGlobalProxy, boolean isGFWList, boolean isBypassApps, public Config(boolean isGlobalProxy, boolean isGFWList, boolean isBypassApps,
boolean isTrafficStat, boolean isUdpDns, String profileName, String proxy, String sitekey, boolean isTrafficStat, boolean isUdpDns, boolean isAuth, boolean isIpv6, String profileName, String proxy, String sitekey,
String encMethod, String proxiedAppString, String route, int remotePort, int localPort) { String encMethod, String proxiedAppString, String route, int remotePort, int localPort) {
this.isGlobalProxy = isGlobalProxy; this.isGlobalProxy = isGlobalProxy;
this.isGFWList = isGFWList; this.isGFWList = isGFWList;
this.isBypassApps = isBypassApps; this.isBypassApps = isBypassApps;
this.isTrafficStat = isTrafficStat; this.isTrafficStat = isTrafficStat;
this.isUdpDns = isUdpDns; this.isUdpDns = isUdpDns;
this.isAuth = isAuth;
this.isIpv6 = isIpv6;
this.profileName = profileName; this.profileName = profileName;
this.proxy = proxy; this.proxy = proxy;
this.sitekey = sitekey; this.sitekey = sitekey;
@@ -60,6 +64,8 @@ public class Config implements Parcelable {
isBypassApps = in.readInt() == 1; isBypassApps = in.readInt() == 1;
isTrafficStat = in.readInt() == 1; isTrafficStat = in.readInt() == 1;
isUdpDns = in.readInt() == 1; isUdpDns = in.readInt() == 1;
isAuth = in.readInt() == 1;
isIpv6 = in.readInt() == 1;
profileName = in.readString(); profileName = in.readString();
proxy = in.readString(); proxy = in.readString();
sitekey = in.readString(); sitekey = in.readString();
@@ -80,6 +86,8 @@ public class Config implements Parcelable {
out.writeInt(isBypassApps ? 1 : 0); out.writeInt(isBypassApps ? 1 : 0);
out.writeInt(isTrafficStat ? 1 : 0); out.writeInt(isTrafficStat ? 1 : 0);
out.writeInt(isUdpDns ? 1 : 0); out.writeInt(isUdpDns ? 1 : 0);
out.writeInt(isAuth ? 1 : 0);
out.writeInt(isIpv6 ? 1 : 0);
out.writeString(profileName); out.writeString(profileName);
out.writeString(proxy); out.writeString(proxy);
out.writeString(sitekey); out.writeString(sitekey);
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
/**
* Math routines similar to those found in {@link java.lang.Math}.
*
* <p>Historically these methods were faster than the equivalent double-based
* {@link java.lang.Math} methods. On versions of Android with a JIT they
* became slower and have since been re-implemented to wrap calls to
* {@link java.lang.Math}. {@link java.lang.Math} should be used in
* preference.
*
* @deprecated Use {@link java.lang.Math} instead.
*/
@Deprecated
public class FloatMath {
/** Prevents instantiation. */
private FloatMath() {}
/**
* Returns the float conversion of the most positive (i.e. closest to
* positive infinity) integer value which is less than the argument.
*
* @param value to be converted
* @return the floor of value
*/
public static float floor(float value) {
return (float) Math.floor(value);
}
/**
* Returns the float conversion of the most negative (i.e. closest to
* negative infinity) integer value which is greater than the argument.
*
* @param value to be converted
* @return the ceiling of value
*/
public static float ceil(float value) {
return (float) Math.ceil(value);
}
/**
* Returns the closest float approximation of the sine of the argument.
*
* @param angle to compute the cosine of, in radians
* @return the sine of angle
*/
public static float sin(float angle) {
return (float) Math.sin(angle);
}
/**
* Returns the closest float approximation of the cosine of the argument.
*
* @param angle to compute the cosine of, in radians
* @return the cosine of angle
*/
public static float cos(float angle) {
return (float) Math.cos(angle);
}
/**
* Returns the closest float approximation of the square root of the
* argument.
*
* @param value to compute sqrt of
* @return the square root of value
*/
public static float sqrt(float value) {
return (float) Math.sqrt(value);
}
/**
* Returns the closest float approximation of the raising "e" to the power
* of the argument.
*
* @param value to compute the exponential of
* @return the exponential of value
*/
public static float exp(float value) {
return (float) Math.exp(value);
}
/**
* Returns the closest float approximation of the result of raising {@code
* x} to the power of {@code y}.
*
* @param x the base of the operation.
* @param y the exponent of the operation.
* @return {@code x} to the power of {@code y}.
*/
public static float pow(float x, float y) {
return (float) Math.pow(x, y);
}
/**
* Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
* {@code y}</i><sup>{@code 2}</sup>{@code )}.
*
* @param x a float number
* @param y a float number
* @return the hypotenuse
*/
public static float hypot(float x, float y) {
return (float) Math.hypot(x, y);
}
}
@@ -1772,6 +1772,9 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
// set client not closed // set client not closed
client->client_closed = 0; client->client_closed = 0;
// enable TCP_NODELAY
tcp_nagle_disable(client->pcb);
// setup handler argument // setup handler argument
tcp_arg(client->pcb, client); tcp_arg(client->pcb, client);
+6
View File
@@ -77,6 +77,12 @@
<string name="udp_dns">UDP 转发</string> <string name="udp_dns">UDP 转发</string>
<string name="udp_dns_summary">由远程服务器转发 UDP 协议的数据包</string> <string name="udp_dns_summary">由远程服务器转发 UDP 协议的数据包</string>
<string name="onetime_auth">一次性认证</string>
<string name="onetime_auth_summary">启用一次性认证(实验性)</string>
<string name="ipv6">IPv6 路由</string>
<string name="ipv6_summary">向远程服务器转发 IPv6 流量</string>
<!-- profile --> <!-- profile -->
<string name="add_profile_dialog">为影梭添加此配置文件?</string> <string name="add_profile_dialog">为影梭添加此配置文件?</string>
<string-array name="add_profile_methods"> <string-array name="add_profile_methods">
+292 -2
View File
@@ -321,6 +321,10 @@
<item>43.239.0.0/19</item> <item>43.239.0.0/19</item>
<item>43.239.32.0/20</item> <item>43.239.32.0/20</item>
<item>43.239.48.0/22</item> <item>43.239.48.0/22</item>
<item>43.239.116.0/22</item>
<item>43.239.120.0/22</item>
<item>43.239.172.0/22</item>
<item>43.239.176.0/22</item>
<item>43.240.0.0/22</item> <item>43.240.0.0/22</item>
<item>43.240.48.0/22</item> <item>43.240.48.0/22</item>
<item>43.240.56.0/21</item> <item>43.240.56.0/21</item>
@@ -505,6 +509,154 @@
<item>43.255.232.0/22</item> <item>43.255.232.0/22</item>
<item>43.255.244.0/22</item> <item>43.255.244.0/22</item>
<item>45.64.112.0/23</item> <item>45.64.112.0/23</item>
<item>45.112.132.0/22</item>
<item>45.112.188.0/22</item>
<item>45.112.208.0/20</item>
<item>45.112.228.0/22</item>
<item>45.112.232.0/21</item>
<item>45.113.12.0/22</item>
<item>45.113.16.0/20</item>
<item>45.113.40.0/22</item>
<item>45.113.52.0/22</item>
<item>45.113.56.0/22</item>
<item>45.113.72.0/22</item>
<item>45.113.108.0/22</item>
<item>45.113.144.0/21</item>
<item>45.113.168.0/22</item>
<item>45.113.176.0/22</item>
<item>45.113.184.0/22</item>
<item>45.113.200.0/21</item>
<item>45.113.208.0/20</item>
<item>45.113.228.0/22</item>
<item>45.113.240.0/22</item>
<item>45.113.252.0/22</item>
<item>45.114.0.0/22</item>
<item>45.114.12.0/22</item>
<item>45.114.32.0/22</item>
<item>45.114.40.0/22</item>
<item>45.114.52.0/22</item>
<item>45.114.96.0/22</item>
<item>45.114.104.0/22</item>
<item>45.114.136.0/22</item>
<item>45.114.196.0/22</item>
<item>45.114.200.0/22</item>
<item>45.114.228.0/22</item>
<item>45.114.236.0/22</item>
<item>45.114.252.0/22</item>
<item>45.115.44.0/22</item>
<item>45.115.100.0/22</item>
<item>45.115.120.0/22</item>
<item>45.115.132.0/22</item>
<item>45.115.144.0/22</item>
<item>45.115.156.0/22</item>
<item>45.115.164.0/22</item>
<item>45.115.200.0/22</item>
<item>45.115.212.0/22</item>
<item>45.115.216.0/22</item>
<item>45.115.228.0/22</item>
<item>45.115.236.0/22</item>
<item>45.115.244.0/22</item>
<item>45.115.248.0/22</item>
<item>45.116.12.0/22</item>
<item>45.116.16.0/21</item>
<item>45.116.24.0/22</item>
<item>45.116.32.0/21</item>
<item>45.116.52.0/22</item>
<item>45.116.60.0/22</item>
<item>45.116.64.0/22</item>
<item>45.116.96.0/21</item>
<item>45.116.140.0/22</item>
<item>45.116.152.0/22</item>
<item>45.116.208.0/22</item>
<item>45.117.8.0/22</item>
<item>45.117.20.0/22</item>
<item>45.117.40.0/22</item>
<item>45.117.68.0/22</item>
<item>45.117.124.0/22</item>
<item>45.117.252.0/22</item>
<item>45.119.52.0/22</item>
<item>45.119.60.0/22</item>
<item>45.119.64.0/21</item>
<item>45.119.72.0/22</item>
<item>45.119.104.0/22</item>
<item>45.119.116.0/22</item>
<item>45.119.160.0/22</item>
<item>45.119.232.0/22</item>
<item>45.120.100.0/22</item>
<item>45.120.140.0/22</item>
<item>45.120.156.0/22</item>
<item>45.120.164.0/22</item>
<item>45.120.220.0/22</item>
<item>45.120.240.0/22</item>
<item>45.121.20.0/22</item>
<item>45.121.52.0/22</item>
<item>45.121.64.0/21</item>
<item>45.121.72.0/22</item>
<item>45.121.92.0/22</item>
<item>45.121.96.0/22</item>
<item>45.121.104.0/22</item>
<item>45.121.172.0/22</item>
<item>45.121.176.0/22</item>
<item>45.121.212.0/22</item>
<item>45.121.240.0/20</item>
<item>45.122.0.0/19</item>
<item>45.122.32.0/21</item>
<item>45.122.40.0/22</item>
<item>45.122.60.0/22</item>
<item>45.122.64.0/19</item>
<item>45.122.96.0/20</item>
<item>45.122.112.0/21</item>
<item>45.122.160.0/19</item>
<item>45.122.192.0/20</item>
<item>45.122.208.0/21</item>
<item>45.122.216.0/22</item>
<item>45.123.28.0/22</item>
<item>45.123.32.0/21</item>
<item>45.123.44.0/22</item>
<item>45.123.48.0/20</item>
<item>45.123.64.0/20</item>
<item>45.123.80.0/21</item>
<item>45.123.88.0/22</item>
<item>45.123.120.0/22</item>
<item>45.123.128.0/21</item>
<item>45.123.136.0/22</item>
<item>45.123.148.0/22</item>
<item>45.123.152.0/21</item>
<item>45.123.164.0/22</item>
<item>45.123.168.0/21</item>
<item>45.123.176.0/21</item>
<item>45.123.184.0/22</item>
<item>45.123.204.0/22</item>
<item>45.123.212.0/22</item>
<item>45.123.224.0/19</item>
<item>45.124.0.0/22</item>
<item>45.124.20.0/22</item>
<item>45.124.28.0/22</item>
<item>45.124.32.0/21</item>
<item>45.124.44.0/22</item>
<item>45.124.68.0/22</item>
<item>45.124.76.0/22</item>
<item>45.124.80.0/22</item>
<item>45.124.100.0/22</item>
<item>45.124.124.0/22</item>
<item>45.124.172.0/22</item>
<item>45.124.176.0/22</item>
<item>45.124.208.0/22</item>
<item>45.124.248.0/21</item>
<item>45.125.12.0/22</item>
<item>45.125.16.0/22</item>
<item>45.125.24.0/21</item>
<item>45.125.32.0/22</item>
<item>45.125.44.0/22</item>
<item>45.125.52.0/22</item>
<item>45.125.56.0/22</item>
<item>45.125.76.0/22</item>
<item>45.125.80.0/20</item>
<item>45.125.96.0/21</item>
<item>45.125.104.0/22</item>
<item>45.125.136.0/22</item>
<item>47.92.0.0/14</item>
<item>47.96.0.0/11</item>
<item>49.4.0.0/14</item> <item>49.4.0.0/14</item>
<item>49.51.0.0/16</item> <item>49.51.0.0/16</item>
<item>49.52.0.0/14</item> <item>49.52.0.0/14</item>
@@ -803,7 +955,6 @@
<item>103.30.96.0/22</item> <item>103.30.96.0/22</item>
<item>103.30.148.0/22</item> <item>103.30.148.0/22</item>
<item>103.30.200.0/22</item> <item>103.30.200.0/22</item>
<item>103.30.216.0/22</item>
<item>103.30.228.0/22</item> <item>103.30.228.0/22</item>
<item>103.30.236.0/22</item> <item>103.30.236.0/22</item>
<item>103.31.0.0/22</item> <item>103.31.0.0/22</item>
@@ -818,6 +969,8 @@
<item>103.35.0.0/19</item> <item>103.35.0.0/19</item>
<item>103.35.32.0/20</item> <item>103.35.32.0/20</item>
<item>103.35.48.0/22</item> <item>103.35.48.0/22</item>
<item>103.35.104.0/22</item>
<item>103.35.116.0/22</item>
<item>103.36.20.0/22</item> <item>103.36.20.0/22</item>
<item>103.36.28.0/22</item> <item>103.36.28.0/22</item>
<item>103.36.36.0/22</item> <item>103.36.36.0/22</item>
@@ -1004,6 +1157,142 @@
<item>103.50.224.0/20</item> <item>103.50.224.0/20</item>
<item>103.50.240.0/21</item> <item>103.50.240.0/21</item>
<item>103.50.248.0/22</item> <item>103.50.248.0/22</item>
<item>103.52.40.0/22</item>
<item>103.52.72.0/21</item>
<item>103.52.80.0/21</item>
<item>103.52.96.0/21</item>
<item>103.52.104.0/22</item>
<item>103.52.160.0/21</item>
<item>103.52.172.0/22</item>
<item>103.52.176.0/22</item>
<item>103.52.184.0/22</item>
<item>103.52.196.0/22</item>
<item>103.53.4.0/22</item>
<item>103.53.64.0/21</item>
<item>103.53.92.0/22</item>
<item>103.53.100.0/22</item>
<item>103.53.124.0/22</item>
<item>103.53.128.0/20</item>
<item>103.53.144.0/22</item>
<item>103.53.160.0/22</item>
<item>103.53.180.0/22</item>
<item>103.53.204.0/22</item>
<item>103.53.208.0/22</item>
<item>103.53.216.0/22</item>
<item>103.53.236.0/22</item>
<item>103.53.248.0/22</item>
<item>103.54.8.0/22</item>
<item>103.54.48.0/22</item>
<item>103.54.60.0/22</item>
<item>103.54.160.0/21</item>
<item>103.54.212.0/22</item>
<item>103.54.228.0/22</item>
<item>103.54.240.0/22</item>
<item>103.55.24.0/22</item>
<item>103.55.80.0/22</item>
<item>103.55.120.0/22</item>
<item>103.55.152.0/22</item>
<item>103.55.172.0/22</item>
<item>103.55.204.0/22</item>
<item>103.55.208.0/22</item>
<item>103.55.228.0/22</item>
<item>103.55.236.0/22</item>
<item>103.55.240.0/22</item>
<item>103.56.8.0/22</item>
<item>103.56.16.0/21</item>
<item>103.56.32.0/22</item>
<item>103.56.52.0/22</item>
<item>103.56.56.0/21</item>
<item>103.56.72.0/21</item>
<item>103.56.100.0/22</item>
<item>103.56.104.0/22</item>
<item>103.56.140.0/22</item>
<item>103.56.152.0/22</item>
<item>103.56.184.0/22</item>
<item>103.56.200.0/22</item>
<item>103.56.216.0/22</item>
<item>103.57.12.0/22</item>
<item>103.57.52.0/22</item>
<item>103.57.56.0/22</item>
<item>103.57.76.0/22</item>
<item>103.57.108.0/22</item>
<item>103.57.136.0/22</item>
<item>103.57.196.0/22</item>
<item>103.58.24.0/22</item>
<item>103.58.182.0/23</item>
<item>103.59.76.0/22</item>
<item>103.59.100.0/22</item>
<item>103.59.112.0/20</item>
<item>103.59.128.0/22</item>
<item>103.59.148.0/22</item>
<item>103.59.164.0/22</item>
<item>103.59.216.0/22</item>
<item>103.60.32.0/22</item>
<item>103.60.44.0/22</item>
<item>103.60.164.0/22</item>
<item>103.60.228.0/22</item>
<item>103.60.236.0/22</item>
<item>103.61.60.0/22</item>
<item>103.61.104.0/22</item>
<item>103.61.140.0/22</item>
<item>103.61.152.0/21</item>
<item>103.61.160.0/22</item>
<item>103.61.172.0/22</item>
<item>103.61.176.0/22</item>
<item>103.61.184.0/21</item>
<item>103.62.24.0/22</item>
<item>103.62.52.0/22</item>
<item>103.62.72.0/21</item>
<item>103.62.80.0/21</item>
<item>103.62.88.0/22</item>
<item>103.62.96.0/19</item>
<item>103.62.128.0/21</item>
<item>103.62.156.0/22</item>
<item>103.62.160.0/19</item>
<item>103.62.192.0/22</item>
<item>103.62.204.0/22</item>
<item>103.62.208.0/20</item>
<item>103.62.224.0/22</item>
<item>103.63.32.0/19</item>
<item>103.63.64.0/20</item>
<item>103.63.80.0/21</item>
<item>103.63.88.0/22</item>
<item>103.63.140.0/22</item>
<item>103.63.144.0/22</item>
<item>103.63.152.0/22</item>
<item>103.63.160.0/20</item>
<item>103.63.176.0/21</item>
<item>103.63.184.0/22</item>
<item>103.63.192.0/20</item>
<item>103.63.208.0/22</item>
<item>103.63.240.0/20</item>
<item>103.192.0.0/19</item>
<item>103.192.48.0/21</item>
<item>103.192.56.0/22</item>
<item>103.192.84.0/22</item>
<item>103.192.88.0/21</item>
<item>103.192.96.0/20</item>
<item>103.192.112.0/22</item>
<item>103.192.128.0/20</item>
<item>103.192.144.0/22</item>
<item>103.192.164.0/22</item>
<item>103.192.188.0/22</item>
<item>103.192.208.0/21</item>
<item>103.192.216.0/22</item>
<item>103.192.252.0/22</item>
<item>103.193.40.0/21</item>
<item>103.193.120.0/21</item>
<item>103.193.140.0/22</item>
<item>103.193.144.0/21</item>
<item>103.193.160.0/22</item>
<item>103.193.188.0/22</item>
<item>103.193.192.0/22</item>
<item>103.193.212.0/22</item>
<item>103.193.216.0/21</item>
<item>103.193.224.0/20</item>
<item>103.193.240.0/22</item>
<item>103.194.16.0/22</item>
<item>103.194.230.0/23</item>
<item>103.224.40.0/21</item> <item>103.224.40.0/21</item>
<item>103.224.60.0/22</item> <item>103.224.60.0/22</item>
<item>103.224.80.0/22</item> <item>103.224.80.0/22</item>
@@ -1096,6 +1385,7 @@
<item>103.236.0.0/18</item> <item>103.236.0.0/18</item>
<item>103.236.64.0/19</item> <item>103.236.64.0/19</item>
<item>103.236.96.0/22</item> <item>103.236.96.0/22</item>
<item>103.236.120.0/22</item>
<item>103.237.0.0/20</item> <item>103.237.0.0/20</item>
<item>103.237.24.0/21</item> <item>103.237.24.0/21</item>
<item>103.237.68.0/22</item> <item>103.237.68.0/22</item>
@@ -1936,6 +2226,7 @@
<item>166.111.0.0/16</item> <item>166.111.0.0/16</item>
<item>167.139.0.0/16</item> <item>167.139.0.0/16</item>
<item>167.189.0.0/16</item> <item>167.189.0.0/16</item>
<item>167.220.244.0/22</item>
<item>168.160.0.0/16</item> <item>168.160.0.0/16</item>
<item>169.254.0.0/16</item> <item>169.254.0.0/16</item>
<item>171.8.0.0/13</item> <item>171.8.0.0/13</item>
@@ -3363,7 +3654,6 @@
<item>210.26.0.0/15</item> <item>210.26.0.0/15</item>
<item>210.28.0.0/14</item> <item>210.28.0.0/14</item>
<item>210.32.0.0/12</item> <item>210.32.0.0/12</item>
<item>210.48.136.0/21</item>
<item>210.51.0.0/16</item> <item>210.51.0.0/16</item>
<item>210.52.0.0/15</item> <item>210.52.0.0/15</item>
<item>210.56.192.0/19</item> <item>210.56.192.0/19</item>
+6 -2
View File
@@ -25,10 +25,14 @@
<!-- feature category --> <!-- feature category -->
<string name="feature_cat">Feature Settings</string> <string name="feature_cat">Feature Settings</string>
<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 (Experimental)</string>
<string name="http_proxy">HTTP Proxy</string> <string name="http_proxy">HTTP Proxy</string>
<string name="http_proxy_summary">Local HTTP Proxy</string> <string name="http_proxy_summary">Local HTTP Proxy</string>
<string name="dns_proxy">DNS Proxy</string> <string name="dns_proxy">DNS Proxy</string>
<string name="dns_proxy_summary">Redirect DNS queries to remote server</string> <string name="dns_proxy_summary">Redirect DNS queries to remote</string>
<string name="auto_set_gfwlist">CHN Route</string> <string name="auto_set_gfwlist">CHN Route</string>
<string name="auto_set_gfwlist_summary">Bypass all sites located in China <string name="auto_set_gfwlist_summary">Bypass all sites located in China
</string> </string>
@@ -46,7 +50,7 @@
<string name="traffic_stat">Traffic Stats</string> <string name="traffic_stat">Traffic Stats</string>
<string name="traffic_stat_summary">Show traffic stats in notification bar</string> <string name="traffic_stat_summary">Show traffic stats in notification bar</string>
<string name="udp_dns">UDP Forwarding</string> <string name="udp_dns">UDP Forwarding</string>
<string name="udp_dns_summary">Forward UDP packets to remote server</string> <string name="udp_dns_summary">Forward UDP packets to remote</string>
<!-- notification category --> <!-- notification category -->
<string name="forward_success">Shadowsocks started.</string> <string name="forward_success">Shadowsocks started.</string>
+12
View File
@@ -58,6 +58,12 @@
android:summary="@string/route_list_summary" android:summary="@string/route_list_summary"
android:title="@string/route_list"> android:title="@string/route_list">
</com.github.shadowsocks.preferences.SummaryListPreference> </com.github.shadowsocks.preferences.SummaryListPreference>
<CheckBoxPreference
android:key="isIpv6"
android:defaultValue="false"
android:summary="@string/ipv6_summary"
android:title="@string/ipv6">
</CheckBoxPreference>
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="true" android:defaultValue="true"
android:key="isGlobalProxy" android:key="isGlobalProxy"
@@ -78,6 +84,12 @@
android:summary="@string/udp_dns_summary" android:summary="@string/udp_dns_summary"
android:title="@string/udp_dns"> android:title="@string/udp_dns">
</CheckBoxPreference> </CheckBoxPreference>
<CheckBoxPreference
android:key="isAuth"
android:defaultValue="false"
android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth">
</CheckBoxPreference>
<CheckBoxPreference <CheckBoxPreference
android:key="isAutoConnect" android:key="isAutoConnect"
android:summary="@string/auto_connect_summary" android:summary="@string/auto_connect_summary"
@@ -65,7 +65,8 @@ import com.nostra13.universalimageloader.core.{DisplayImageOptions, ImageLoader,
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
import com.github.shadowsocks.utils.{Utils, Scheme, Key} import com.github.shadowsocks.utils.{Utils, Scheme, Key}
import scala.concurrent.ops._ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.language.implicitConversions import scala.language.implicitConversions
case class ProxiedApp(uid: Int, name: String, packageName: String, var proxied: Boolean) case class ProxiedApp(uid: Int, name: String, packageName: String, var proxied: Boolean)
@@ -267,9 +268,8 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
} }
protected override def onCreateOptionsMenu(menu: Menu): Boolean = { protected override def onCreateOptionsMenu(menu: Menu): Boolean = {
val inflater = getMenuInflater() getMenuInflater.inflate(R.menu.app_manager_menu, menu)
inflater.inflate(R.menu.app_manager_menu, menu) super.onCreateOptionsMenu(menu)
return super.onCreateOptionsMenu(menu)
} }
protected override def onCreate(savedInstanceState: Bundle) { protected override def onCreate(savedInstanceState: Bundle) {
@@ -277,7 +277,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
handler = new Handler() handler = new Handler()
val actionBar = getActionBar() val actionBar = getActionBar
actionBar.setTitle(R.string.proxied_help) actionBar.setTitle(R.string.proxied_help)
actionBar.setDisplayHomeAsUpEnabled(true) actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setDisplayShowHomeEnabled(false) actionBar.setDisplayShowHomeEnabled(false)
@@ -297,11 +297,8 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch] val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext) val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext)
bypassSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener { bypassSwitch.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) =>
def onCheckedChanged(button: CompoundButton, checked: Boolean) { prefs.edit().putBoolean(Key.isBypassApps, checked).commit())
prefs.edit().putBoolean(Key.isBypassApps, checked).commit()
}
})
bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false)) bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false))
appListView = findViewById(R.id.applistview).asInstanceOf[ListView] appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
@@ -309,7 +306,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
protected override def onResume() { protected override def onResume() {
super.onResume() super.onResume()
spawn { Future {
handler.post(loadStartRunnable) handler.post(loadStartRunnable)
if (!appsLoaded) loadApps() if (!appsLoaded) loadApps()
handler.post(loadFinishRunnable) handler.post(loadFinishRunnable)
@@ -39,13 +39,11 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.os.{Handler, RemoteCallbackList}
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils.{Path, State}
import java.io.{IOException, FileNotFoundException, FileReader, BufferedReader}
import android.util.Log
import android.app.Notification import android.app.Notification
import android.content.Context import android.content.Context
import android.os.{Handler, RemoteCallbackList}
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils.State
trait BaseService { trait BaseService {
@@ -100,10 +98,10 @@ trait BaseService {
def getTag: String def getTag: String
def getContext: Context def getContext: Context
def getCallbackCount(): Int = { def getCallbackCount: Int = {
callbackCount callbackCount
} }
def getState(): Int = { def getState: Int = {
state state
} }
def changeState(s: Int) { def changeState(s: Int) {
@@ -112,23 +110,19 @@ trait BaseService {
protected def changeState(s: Int, msg: String) { protected def changeState(s: Int, msg: String) {
val handler = new Handler(getContext.getMainLooper) val handler = new Handler(getContext.getMainLooper)
handler.post(new Runnable { handler.post(() => if (state != s) {
override def run() { if (callbackCount > 0) {
if (state != s) { val n = callbacks.beginBroadcast()
if (callbackCount > 0) { for (i <- 0 to n - 1) {
val n = callbacks.beginBroadcast() try {
for (i <- 0 to n - 1) { callbacks.getBroadcastItem(i).stateChanged(s, msg)
try { } catch {
callbacks.getBroadcastItem(i).stateChanged(s, msg) case _: Exception => // Ignore
} catch {
case _: Exception => // Ignore
}
}
callbacks.finishBroadcast()
} }
state = s
} }
callbacks.finishBroadcast()
} }
state = s
}) })
} }
@@ -39,15 +39,15 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.os.{Message, Handler, Bundle} import android.app.{Activity, AlertDialog, ProgressDialog}
import android.app.{ProgressDialog, AlertDialog, Activity} import android.content.DialogInterface
import android.content.{Intent, DialogInterface}
import com.github.shadowsocks.database.{ProfileManager, Profile}
import com.github.shadowsocks.utils.{Parser, Action}
import android.preference.PreferenceManager
import android.view.WindowManager
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.{Bundle, Handler, Message}
import android.preference.PreferenceManager
import android.view.WindowManager
import com.github.shadowsocks.database.{Profile, ProfileManager}
import com.github.shadowsocks.utils.Parser
class ParserActivity extends Activity { class ParserActivity extends Activity {
override def onCreate(savedInstanceState: Bundle) { override def onCreate(savedInstanceState: Bundle) {
@@ -57,21 +57,17 @@ class ParserActivity extends Activity {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.add_profile_dialog) .setTitle(R.string.add_profile_dialog)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.yes, ((dialog: DialogInterface, id: Int) => {
override def onClick(dialog: DialogInterface, id: Int) {
Parser.parse(data) match { Parser.parse(data) match {
case Some(profile) => addProfile(profile) case Some(profile) => addProfile(profile)
case _ => // ignore case _ => // ignore
} }
dialog.dismiss() dialog.dismiss()
} }): DialogInterface.OnClickListener)
}) .setNegativeButton(R.string.no, ((dialog: DialogInterface, id: Int) => {
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) {
dialog.dismiss() dialog.dismiss()
finish() finish()
} }): DialogInterface.OnClickListener)
})
.setMessage(data) .setMessage(data)
.create() .create()
.show() .show()
@@ -91,15 +87,13 @@ class ParserActivity extends Activity {
val h = showProgress(getString(R.string.loading)) val h = showProgress(getString(R.string.loading))
h.postDelayed(new Runnable { h.postDelayed(() => {
def run() { val profileManager =
val profileManager = new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext),
new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext), getApplication.asInstanceOf[ShadowsocksApplication].dbHelper)
getApplication.asInstanceOf[ShadowsocksApplication].dbHelper) profileManager.createOrUpdateProfile(profile)
profileManager.createOrUpdateProfile(profile) profileManager.reload(profile.id)
profileManager.reload(profile.id) h.sendEmptyMessage(0)
h.sendEmptyMessage(0)
}
}, 600) }, 600)
} }
@@ -38,10 +38,9 @@
*/ */
package com.github.shadowsocks package com.github.shadowsocks
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, FileOutputStream, IOException, InputStream, OutputStream}
import java.util import java.util
import java.io.{OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream, IOException, FileOutputStream}
import java.util.Locale import java.util.Locale
import java.lang.Math
import android.app.backup.BackupManager import android.app.backup.BackupManager
import android.app.{Activity, AlertDialog, ProgressDialog} import android.app.{Activity, AlertDialog, ProgressDialog}
@@ -52,8 +51,8 @@ import android.graphics.{Bitmap, Color, Typeface}
import android.net.{Uri, VpnService} import android.net.{Uri, VpnService}
import android.os._ import android.os._
import android.preference._ import android.preference._
import android.support.v4.content.ContextCompat
import android.util.{DisplayMetrics, Log} import android.util.{DisplayMetrics, Log}
import android.view.View.OnLongClickListener
import android.view._ import android.view._
import android.webkit.{WebView, WebViewClient} import android.webkit.{WebView, WebViewClient}
import android.widget._ import android.widget._
@@ -65,15 +64,15 @@ import com.github.shadowsocks.utils._
import com.google.android.gms.ads.{AdRequest, AdSize, AdView} import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
import com.google.android.gms.analytics.HitBuilders import com.google.android.gms.analytics.HitBuilders
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
import net.simonvt.menudrawer.MenuDrawer
import com.joanzapata.android.iconify.Iconify
import com.joanzapata.android.iconify.Iconify.IconValue
import com.joanzapata.android.iconify.IconDrawable import com.joanzapata.android.iconify.IconDrawable
import com.joanzapata.android.iconify.Iconify.IconValue
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
import net.glxn.qrgen.android.QRCode import net.glxn.qrgen.android.QRCode
import net.simonvt.menudrawer.MenuDrawer
import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import scala.concurrent.ops._ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int) class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int)
extends BaseImageDownloader(context, connectTimeout, readTimeout) { extends BaseImageDownloader(context, connectTimeout, readTimeout) {
@@ -127,7 +126,7 @@ object Shadowsocks {
val PROXY_PREFS = Array(Key.profileName, Key.proxy, Key.remotePort, Key.localPort, Key.sitekey, val PROXY_PREFS = Array(Key.profileName, Key.proxy, Key.remotePort, Key.localPort, Key.sitekey,
Key.encMethod) Key.encMethod)
val FEATRUE_PREFS = Array(Key.route, Key.isGlobalProxy, Key.proxyedApps, val FEATRUE_PREFS = Array(Key.route, Key.isGlobalProxy, Key.proxyedApps,
Key.isUdpDns, Key.isAutoConnect) Key.isUdpDns, Key.isAuth, Key.isIpv6, Key.isAutoConnect)
val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL, Executable.TUN2SOCKS) val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL, Executable.TUN2SOCKS)
@@ -166,6 +165,8 @@ object Shadowsocks {
case Key.route => updateListPreference(pref, profile.route) case Key.route => updateListPreference(pref, profile.route)
case Key.isGlobalProxy => updateCheckBoxPreference(pref, profile.global) case Key.isGlobalProxy => updateCheckBoxPreference(pref, profile.global)
case Key.isUdpDns => updateCheckBoxPreference(pref, profile.udpdns) case Key.isUdpDns => updateCheckBoxPreference(pref, profile.udpdns)
case Key.isAuth => updateCheckBoxPreference(pref, profile.auth)
case Key.isIpv6 => updateCheckBoxPreference(pref, profile.ipv6)
case _ => case _ =>
} }
} }
@@ -222,7 +223,7 @@ class Shadowsocks
if (!status.getBoolean(getVersionName, false)) { if (!status.getBoolean(getVersionName, false)) {
status.edit.putBoolean(getVersionName, true).commit() status.edit.putBoolean(getVersionName, true).commit()
recovery(); recovery()
} }
} }
@@ -248,7 +249,7 @@ class Shadowsocks
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication] private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
var handler: Handler = null var handler = new Handler()
def isSinglePane: Boolean = { def isSinglePane: Boolean = {
if (singlePane == -1) { if (singlePane == -1) {
@@ -268,11 +269,7 @@ class Shadowsocks
switchButton.setChecked(checked) switchButton.setChecked(checked)
if (switchButton.isEnabled) { if (switchButton.isEnabled) {
switchButton.setEnabled(false) switchButton.setEnabled(false)
handler.postDelayed(new Runnable { handler.postDelayed(() => switchButton.setEnabled(true), 1000)
override def run() {
switchButton.setEnabled(true)
}
}, 1000)
} }
switchButton.setOnCheckedChangeListener(this) switchButton.setOnCheckedChangeListener(this)
} }
@@ -394,7 +391,7 @@ class Shadowsocks
changeSwitch(checked = false) changeSwitch(checked = false)
} }
def isReady(): Boolean = { def isReady: Boolean = {
if (!checkText(Key.proxy)) return false if (!checkText(Key.proxy)) return false
if (!checkText(Key.sitekey)) return false if (!checkText(Key.sitekey)) return false
if (!checkNumber(Key.localPort, low = false)) return false if (!checkNumber(Key.localPort, low = false)) return false
@@ -405,7 +402,7 @@ class Shadowsocks
def prepareStartService() { def prepareStartService() {
showProgress(R.string.connecting) showProgress(R.string.connecting)
spawn { Future {
if (isVpnEnabled) { if (isVpnEnabled) {
val intent = VpnService.prepare(this) val intent = VpnService.prepare(this)
if (intent != null) { if (intent != null) {
@@ -432,11 +429,7 @@ class Shadowsocks
} }
if (switchButton.isEnabled) { if (switchButton.isEnabled) {
switchButton.setEnabled(false) switchButton.setEnabled(false)
handler.postDelayed(new Runnable { handler.postDelayed(() => switchButton.setEnabled(true), 1000)
override def run() {
switchButton.setEnabled(true)
}
}, 1000)
} }
} }
} }
@@ -477,8 +470,6 @@ class Shadowsocks
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
handler = new Handler()
addPreferencesFromResource(R.xml.pref_all) addPreferencesFromResource(R.xml.pref_all)
// Update the profile // Update the profile
@@ -522,28 +513,26 @@ class Shadowsocks
getActionBar.setDisplayShowTitleEnabled(false) getActionBar.setDisplayShowTitleEnabled(false)
getActionBar.setDisplayShowCustomEnabled(true) getActionBar.setDisplayShowCustomEnabled(true)
if (Utils.isLollipopOrAbove) { if (Utils.isLollipopOrAbove) {
getWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); getWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
getWindow.setStatusBarColor(getResources().getColor(R.color.grey3)); getWindow.setStatusBarColor(ContextCompat.getColor(this, R.color.grey3))
getActionBar.setDisplayHomeAsUpEnabled(true) getActionBar.setDisplayHomeAsUpEnabled(true)
getActionBar.setHomeAsUpIndicator(R.drawable.ic_drawer) getActionBar.setHomeAsUpIndicator(R.drawable.ic_drawer)
} else { } else {
getActionBar.setIcon(R.drawable.ic_stat_shadowsocks) getActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
} }
title.setOnLongClickListener(new OnLongClickListener { title.setOnLongClickListener((v: View) => {
override def onLongClick(v: View): Boolean = { if (Utils.isLollipopOrAbove && bgService != null
if (Utils.isLollipopOrAbove && bgService != null && (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) {
&& (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) { val natEnabled = status.getBoolean(Key.isNAT, false)
val natEnabled = status.getBoolean(Key.isNAT, false) status.edit().putBoolean(Key.isNAT, !natEnabled).commit()
status.edit().putBoolean(Key.isNAT, !natEnabled).commit() if (!natEnabled) {
if (!natEnabled) { Toast.makeText(getBaseContext, R.string.enable_nat, Toast.LENGTH_LONG).show()
Toast.makeText(getBaseContext, R.string.enable_nat, Toast.LENGTH_LONG).show()
} else {
Toast.makeText(getBaseContext, R.string.disable_nat, Toast.LENGTH_LONG).show()
}
true
} else { } else {
false Toast.makeText(getBaseContext, R.string.disable_nat, Toast.LENGTH_LONG).show()
} }
true
} else {
false
} }
}) })
@@ -551,13 +540,11 @@ class Shadowsocks
registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS)) registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS))
// Bind to the service // Bind to the service
spawn { Future {
val isRoot = (!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot val isRoot = (!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot
handler.post(new Runnable { handler.post(() => {
override def run() { status.edit.putBoolean(Key.isRoot, isRoot).commit()
status.edit.putBoolean(Key.isRoot, isRoot).commit() attachService()
attachService()
}
}) })
} }
} }
@@ -613,29 +600,23 @@ class Shadowsocks
val builder = new AlertDialog.Builder(this) val builder = new AlertDialog.Builder(this)
builder builder
.setTitle(R.string.add_profile) .setTitle(R.string.add_profile)
.setItems(R.array.add_profile_methods, new DialogInterface.OnClickListener() { .setItems(R.array.add_profile_methods, ((dialog: DialogInterface, which: Int) => which match {
def onClick(dialog: DialogInterface, which: Int) { case 0 =>
which match { dialog.dismiss()
case 0 => val h = showProgress(R.string.loading)
dialog.dismiss() h.postDelayed(() => {
val h = showProgress(R.string.loading) val integrator = new IntentIntegrator(Shadowsocks.this)
h.postDelayed(new Runnable() { val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN)
def run() { list.add("tw.com.quickmark")
val integrator = new IntentIntegrator(Shadowsocks.this) integrator.setTargetApplications(list)
val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN) integrator.initiateScan()
list.add("tw.com.quickmark") h.sendEmptyMessage(0)
integrator.setTargetApplications(list) }, 600)
integrator.initiateScan() case 1 =>
h.sendEmptyMessage(0) dialog.dismiss()
} addProfile(id)
}, 600) case _ =>
case 1 => }): DialogInterface.OnClickListener)
dialog.dismiss()
addProfile(id)
case _ =>
}
}
})
builder.create().show() builder.create().show()
} }
@@ -644,17 +625,15 @@ class Shadowsocks
val h = showProgress(R.string.loading) val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable { handler.postDelayed(() => {
def run() { currentProfile = {
currentProfile = { profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
}
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen()
h.sendEmptyMessage(0)
} }
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen()
h.sendEmptyMessage(0)
}, 600) }, 600)
} }
@@ -663,17 +642,15 @@ class Shadowsocks
val h = showProgress(R.string.loading) val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable { handler.postDelayed(() => {
def run() { currentProfile = profile
currentProfile = profile profileManager.createOrUpdateProfile(currentProfile)
profileManager.createOrUpdateProfile(currentProfile) profileManager.reload(currentProfile.id)
profileManager.reload(currentProfile.id) menuAdapter.updateList(getMenuList, currentProfile.id)
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen() updatePreferenceScreen()
h.sendEmptyMessage(0) h.sendEmptyMessage(0)
}
}, 600) }, 600)
} }
@@ -682,16 +659,14 @@ class Shadowsocks
val h = showProgress(R.string.loading) val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable { handler.postDelayed(() => {
def run() { currentProfile = profileManager.reload(id)
currentProfile = profileManager.reload(id) profileManager.save()
profileManager.save() menuAdapter.updateList(getMenuList, currentProfile.id)
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen() updatePreferenceScreen()
h.sendEmptyMessage(0) h.sendEmptyMessage(0)
}
}, 600) }, 600)
} }
@@ -700,16 +675,14 @@ class Shadowsocks
val h = showProgress(R.string.loading) val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable { handler.postDelayed(() => {
def run() { currentProfile = profileManager.reload(id)
currentProfile = profileManager.reload(id) menuAdapter.setActiveId(id)
menuAdapter.setActiveId(id) menuAdapter.notifyDataSetChanged()
menuAdapter.notifyDataSetChanged()
updatePreferenceScreen() updatePreferenceScreen()
h.sendEmptyMessage(0) h.sendEmptyMessage(0)
}
}, 600) }, 600)
} }
@@ -718,20 +691,18 @@ class Shadowsocks
val profile = profileManager.getProfile(id) val profile = profileManager.getProfile(id)
if (!profile.isDefined) return false if (profile.isEmpty) return false
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), profile.get.name)) .setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), profile.get.name))
.setCancelable(false) .setCancelable(false)
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { .setNegativeButton(R.string.no,
override def onClick(dialog: DialogInterface, i: Int) = dialog.cancel() ((dialog: DialogInterface, i: Int) => dialog.cancel()): DialogInterface.OnClickListener)
}) .setPositiveButton(R.string.yes, ((dialog: DialogInterface, i: Int) => {
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, i: Int) {
profileManager.delProfile(id) profileManager.delProfile(id)
val profileId = { val profileId = {
val profiles = profileManager.getAllProfiles.getOrElse(List[Profile]()) val profiles = profileManager.getAllProfiles.getOrElse(List[Profile]())
if (profiles.isEmpty) -1 else profiles(0).id if (profiles.isEmpty) -1 else profiles.head.id
} }
currentProfile = profileManager.load(profileId) currentProfile = profileManager.load(profileId)
menuAdapter.updateList(getMenuList, currentProfile.id) menuAdapter.updateList(getMenuList, currentProfile.id)
@@ -739,8 +710,7 @@ class Shadowsocks
updatePreferenceScreen() updatePreferenceScreen()
dialog.dismiss() dialog.dismiss()
} }): DialogInterface.OnClickListener)
})
.create() .create()
.show() .show()
@@ -899,10 +869,7 @@ class Shadowsocks
deattachService() deattachService()
unregisterReceiver(preferenceReceiver) unregisterReceiver(preferenceReceiver)
new BackupManager(this).dataChanged() new BackupManager(this).dataChanged()
if (handler != null) { handler.removeCallbacksAndMessages(null)
handler.removeCallbacksAndMessages(null)
handler = null
}
} }
def copyToSystem() { def copyToSystem() {
@@ -937,16 +904,14 @@ class Shadowsocks
private def recovery() { private def recovery() {
serviceStop() serviceStop()
val h = showProgress(R.string.recovering) val h = showProgress(R.string.recovering)
spawn { Future {
reset() reset()
h.sendEmptyMessage(0) h.sendEmptyMessage(0)
} }
} }
private def dp2px(dp: Int): Int = { private def dp2px(dp: Int): Int =
val displayMetrics = getBaseContext.getResources.getDisplayMetrics() Math.round(dp * (getBaseContext.getResources.getDisplayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
}
private def showQrCode() { private def showQrCode() {
val image = new ImageView(this) val image = new ImageView(this)
@@ -958,11 +923,8 @@ class Shadowsocks
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setCancelable(true) .setCancelable(true)
.setNegativeButton(getString(R.string.close), new DialogInterface.OnClickListener() { .setNegativeButton(getString(R.string.close),
override def onClick(dialog: DialogInterface, id: Int) { ((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener)
dialog.cancel()
}
})
.setView(image) .setView(image)
.create() .create()
.show() .show()
@@ -970,7 +932,7 @@ class Shadowsocks
private def flushDnsCache() { private def flushDnsCache() {
val h = showProgress(R.string.flushing) val h = showProgress(R.string.flushing)
spawn { Future {
Utils.toggleAirplaneMode(getBaseContext) Utils.toggleAirplaneMode(getBaseContext)
h.sendEmptyMessage(0) h.sendEmptyMessage(0)
} }
@@ -1073,11 +1035,8 @@ class Shadowsocks
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, versionName)) .setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, versionName))
.setCancelable(false) .setCancelable(false)
.setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener() { .setNegativeButton(getString(R.string.ok_iknow),
override def onClick(dialog: DialogInterface, id: Int) { ((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener)
dialog.cancel()
}
})
.setView(web) .setView(web)
.create() .create()
.show() .show()
@@ -1092,46 +1051,42 @@ class Shadowsocks
} }
def onStateChanged(s: Int, m: String) { def onStateChanged(s: Int, m: String) {
handler.post(new Runnable { handler.post(() => if (state != s) {
override def run() { state = s
if (state != s) { state match {
state = s case State.CONNECTING =>
state match { if (progressDialog == null) {
case State.CONNECTING => progressDialog = ProgressDialog
if (progressDialog == null) { .show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
progressDialog = ProgressDialog progressTag = R.string.connecting
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true) }
progressTag = R.string.connecting setPreferenceEnabled(enabled = false)
} case State.CONNECTED =>
setPreferenceEnabled(enabled = false) if (progressTag == R.string.connecting) {
case State.CONNECTED => clearDialog()
if (progressTag == R.string.connecting) { }
clearDialog() changeSwitch(checked = true)
} setPreferenceEnabled(enabled = false)
changeSwitch(checked = true) case State.STOPPED =>
setPreferenceEnabled(enabled = false) if (progressTag == R.string.stopping || progressTag == R.string.connecting) {
case State.STOPPED => clearDialog()
if (progressTag == R.string.stopping) { }
clearDialog() changeSwitch(checked = false)
} if (m != null) {
changeSwitch(checked = false) new SnackBar.Builder(Shadowsocks.this)
if (m != null) { .withMessage(getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m))
new SnackBar.Builder(Shadowsocks.this) .withActionMessageId(R.string.error)
.withMessage(getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m)) .withStyle(SnackBar.Style.ALERT)
.withActionMessageId(R.string.error) .withDuration(SnackBar.LONG_SNACK)
.withStyle(SnackBar.Style.ALERT) .show()
.withDuration(SnackBar.LONG_SNACK) }
.show() setPreferenceEnabled(enabled = true)
} case State.STOPPING =>
setPreferenceEnabled(enabled = true) if (progressDialog == null) {
case State.STOPPING => progressDialog = ProgressDialog
if (progressDialog == null) { .show(Shadowsocks.this, "", getString(R.string.stopping), true, true)
progressDialog = ProgressDialog progressTag = R.string.stopping
.show(Shadowsocks.this, "", getString(R.string.stopping), true, true)
progressTag = R.string.stopping
}
} }
}
} }
}) })
} }
@@ -47,7 +47,6 @@ import com.github.shadowsocks.database.DBHelper
import com.github.shadowsocks.utils.Utils import com.github.shadowsocks.utils.Utils
import com.google.android.gms.analytics.GoogleAnalytics import com.google.android.gms.analytics.GoogleAnalytics
import com.google.android.gms.common.api.ResultCallback import com.google.android.gms.common.api.ResultCallback
import com.google.android.gms.tagmanager.Container.FunctionCallMacroCallback
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager} import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
class ShadowsocksApplication extends Application { class ShadowsocksApplication extends Application {
@@ -66,14 +65,13 @@ class ShadowsocksApplication extends Application {
} }
containerHolder = holder containerHolder = holder
val container = holder.getContainer val container = holder.getContainer
container.registerFunctionCallMacroCallback(SIG_FUNC, new FunctionCallMacroCallback { container.registerFunctionCallMacroCallback(SIG_FUNC,
override def getValue(functionName: String, parameters: util.Map[String, AnyRef]): AnyRef = { (functionName: String, parameters: util.Map[String, AnyRef]) => {
if (functionName == SIG_FUNC) { if (functionName == SIG_FUNC) {
Utils.getSignature(getApplicationContext) Utils.getSignature(getApplicationContext)
} }
null null
} })
})
} }
} }
pending.setResultCallback(callback, 2, TimeUnit.SECONDS) pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
@@ -41,6 +41,7 @@ package com.github.shadowsocks
import java.io.File import java.io.File
import java.lang.reflect.{InvocationTargetException, Method} import java.lang.reflect.{InvocationTargetException, Method}
import java.net.{Inet6Address, InetAddress}
import java.util.Locale import java.util.Locale
import android.app._ import android.app._
@@ -54,11 +55,11 @@ import android.widget.Toast
import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders import com.google.android.gms.analytics.HitBuilders
import org.apache.http.conn.util.InetAddressUtils
import scala.collection._ import scala.collection._
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ops._ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class ShadowsocksNatService extends Service with BaseService { class ShadowsocksNatService extends Service with BaseService {
@@ -68,25 +69,13 @@ class ShadowsocksNatService extends Service with BaseService {
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " +
"-j DNAT --to-destination 127.0.0.1:8123" "-j DNAT --to-destination 127.0.0.1:8123"
private val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
private val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundArgs = new Array[AnyRef](1)
private val mStartForegroundArgs = new Array[AnyRef](2)
private val mStopForegroundArgs = new Array[AnyRef](1)
var lockReceiver: BroadcastReceiver = null var lockReceiver: BroadcastReceiver = null
var closeReceiver: BroadcastReceiver = null var closeReceiver: BroadcastReceiver = null
var connReceiver: BroadcastReceiver = null var connReceiver: BroadcastReceiver = null
var notificationManager: NotificationManager = null
var config: Config = null var config: Config = null
var apps: Array[ProxiedApp] = null var apps: Array[ProxiedApp] = null
val myUid = Process.myUid() val myUid = Process.myUid()
private var mSetForeground: Method = null
private var mStartForeground: Method = null
private var mStopForeground: Method = null
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication] private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
private val dnsAddressCache = new SparseArray[String] private val dnsAddressCache = new SparseArray[String]
@@ -172,11 +161,7 @@ class ShadowsocksNatService extends Service with BaseService {
def initConnectionReceiver() { def initConnectionReceiver() {
val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
connReceiver = new BroadcastReceiver { connReceiver = (context: Context, intent: Intent) => setupDns()
override def onReceive(context: Context, intent: Intent) = {
setupDns()
}
}
registerReceiver(connReceiver, filter) registerReceiver(connReceiver, filter)
} }
@@ -205,6 +190,8 @@ class ShadowsocksNatService extends Service with BaseService {
, "-c" , Path.BASE + "ss-local-nat.conf" , "-c" , Path.BASE + "ss-local-nat.conf"
, "-f" , Path.BASE + "ss-local-nat.pid") , "-f" , Path.BASE + "ss-local-nat.pid")
if (config.isAuth) cmd += "-A"
if (config.route != Route.ALL) { if (config.route != Route.ALL) {
cmd += "--acl" cmd += "--acl"
cmd += (Path.BASE + "acl.list") cmd += (Path.BASE + "acl.list")
@@ -233,6 +220,8 @@ class ShadowsocksNatService extends Service with BaseService {
cmd += ("-l" , "8153") cmd += ("-l" , "8153")
if (config.isAuth) cmd += "-A"
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
Console.runCommand(cmd.mkString(" ")) Console.runCommand(cmd.mkString(" "))
@@ -254,6 +243,8 @@ class ShadowsocksNatService extends Service with BaseService {
, "-c" , Path.BASE + "ss-tunnel-nat.conf" , "-c" , Path.BASE + "ss-tunnel-nat.conf"
, "-f" , Path.BASE + "ss-tunnel-nat.pid") , "-f" , Path.BASE + "ss-tunnel-nat.pid")
if (config.isAuth) cmdBuf += "-A"
if (BuildConfig.DEBUG) Log.d(TAG, cmdBuf.mkString(" ")) if (BuildConfig.DEBUG) Log.d(TAG, cmdBuf.mkString(" "))
Console.runCommand(cmdBuf.mkString(" ")) Console.runCommand(cmdBuf.mkString(" "))
} }
@@ -265,10 +256,10 @@ class ShadowsocksNatService extends Service with BaseService {
val reject = ConfigUtils.getRejectList(getContext, application) val reject = ConfigUtils.getRejectList(getContext, application)
val blackList = ConfigUtils.getBlackList(getContext, application) val blackList = ConfigUtils.getBlackList(getContext, application)
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153,
Path.BASE + "pdnsd-nat.pid", reject, blackList, 8163) Path.BASE + "pdnsd-nat.pid", reject, blackList, 8163, "")
} else { } else {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153, ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153,
Path.BASE + "pdnsd-nat.pid", 8163) Path.BASE + "pdnsd-nat.pid", 8163, "")
} }
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-nat.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-nat.conf"))(p => {
@@ -318,17 +309,6 @@ class ShadowsocksNatService extends Service with BaseService {
true true
} }
def invokeMethod(method: Method, args: Array[AnyRef]) {
try {
method.invoke(this, mStartForegroundArgs: _*)
} catch {
case e: InvocationTargetException =>
Log.w(TAG, "Unable to invoke method", e)
case e: IllegalAccessException =>
Log.w(TAG, "Unable to invoke method", e)
}
}
def notifyForegroundAlert(title: String, info: String, visible: Boolean) { def notifyForegroundAlert(title: String, info: String, visible: Boolean) {
val openIntent = new Intent(this, classOf[Shadowsocks]) val openIntent = new Intent(this, classOf[Shadowsocks])
openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
@@ -352,7 +332,7 @@ class ShadowsocksNatService extends Service with BaseService {
else else
builder.setPriority(NotificationCompat.PRIORITY_MIN) builder.setPriority(NotificationCompat.PRIORITY_MIN)
startForegroundCompat(1, builder.build) startForeground(1, builder.build)
} }
def onBind(intent: Intent): IBinder = { def onBind(intent: Intent): IBinder = {
@@ -366,29 +346,7 @@ class ShadowsocksNatService extends Service with BaseService {
override def onCreate() { override def onCreate() {
super.onCreate() super.onCreate()
ConfigUtils.refresh(this) ConfigUtils.refresh(this)
notificationManager = this
.getSystemService(Context.NOTIFICATION_SERVICE)
.asInstanceOf[NotificationManager]
try {
mStartForeground = getClass.getMethod("startForeground", mStartForegroundSignature: _*)
mStopForeground = getClass.getMethod("stopForeground", mStopForegroundSignature: _*)
} catch {
case e: NoSuchMethodException =>
mStartForeground = {
mStopForeground = null
mStopForeground
}
}
try {
mSetForeground = getClass.getMethod("setForeground", mSetForegroundSignature: _*)
} catch {
case e: NoSuchMethodException =>
throw new IllegalStateException(
"OS doesn't have Service.startForeground OR Service.setForeground!")
}
} }
def killProcesses() { def killProcesses() {
@@ -424,7 +382,7 @@ class ShadowsocksNatService extends Service with BaseService {
init_sb.append(Utils.getIptables + " -t nat -F OUTPUT") init_sb.append(Utils.getIptables + " -t nat -F OUTPUT")
val cmd_bypass = Utils.getIptables + CMD_IPTABLES_RETURN val cmd_bypass = Utils.getIptables + CMD_IPTABLES_RETURN
if (!InetAddressUtils.isIPv6Address(config.proxy.toUpperCase)) { if (!InetAddress.getByName(config.proxy.toUpperCase).isInstanceOf[Inet6Address]) {
init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d " + config.proxy)) init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d " + config.proxy))
} }
init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d 127.0.0.1")) init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d 127.0.0.1"))
@@ -459,44 +417,6 @@ class ShadowsocksNatService extends Service with BaseService {
Console.runRootCommand(http_sb.toArray) Console.runRootCommand(http_sb.toArray)
} }
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
def startForegroundCompat(id: Int, notification: Notification) {
if (mStartForeground != null) {
mStartForegroundArgs(0) = int2Integer(id)
mStartForegroundArgs(1) = notification
invokeMethod(mStartForeground, mStartForegroundArgs)
return
}
mSetForegroundArgs(0) = boolean2Boolean(x = true)
invokeMethod(mSetForeground, mSetForegroundArgs)
notificationManager.notify(id, notification)
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
def stopForegroundCompat(id: Int) {
if (mStopForeground != null) {
mStopForegroundArgs(0) = boolean2Boolean(x = true)
try {
mStopForeground.invoke(this, mStopForegroundArgs: _*)
} catch {
case e: InvocationTargetException =>
Log.w(TAG, "Unable to invoke stopForeground", e)
case e: IllegalAccessException =>
Log.w(TAG, "Unable to invoke stopForeground", e)
}
return
}
notificationManager.cancel(id)
mSetForegroundArgs(0) = boolean2Boolean(x = false)
invokeMethod(mSetForeground, mSetForegroundArgs)
}
override def startRunner(c: Config) { override def startRunner(c: Config) {
config = c config = c
@@ -505,11 +425,9 @@ class ShadowsocksNatService extends Service with BaseService {
val filter = new IntentFilter() val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE) filter.addAction(Action.CLOSE)
closeReceiver = new BroadcastReceiver() { closeReceiver = (context: Context, intent: Intent) => {
def onReceive(context: Context, intent: Intent) { Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show() stopRunner()
stopRunner()
}
} }
registerReceiver(closeReceiver, filter) registerReceiver(closeReceiver, filter)
@@ -518,26 +436,22 @@ class ShadowsocksNatService extends Service with BaseService {
screenFilter.addAction(Intent.ACTION_SCREEN_ON) screenFilter.addAction(Intent.ACTION_SCREEN_ON)
screenFilter.addAction(Intent.ACTION_SCREEN_OFF) screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
screenFilter.addAction(Intent.ACTION_USER_PRESENT) screenFilter.addAction(Intent.ACTION_USER_PRESENT)
lockReceiver = new BroadcastReceiver() { lockReceiver = (context: Context, intent: Intent) => if (getState == State.CONNECTED) {
def onReceive(context: Context, intent: Intent) { val action = intent.getAction
if (getState == State.CONNECTED) { if (action == Intent.ACTION_SCREEN_OFF) {
val action = intent.getAction notifyForegroundAlert(getString(R.string.forward_success),
if (action == Intent.ACTION_SCREEN_OFF) { getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
notifyForegroundAlert(getString(R.string.forward_success), } else if (action == Intent.ACTION_SCREEN_ON) {
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false) val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager]
} else if (action == Intent.ACTION_SCREEN_ON) { if (!keyGuard.inKeyguardRestrictedInputMode) {
val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] notifyForegroundAlert(getString(R.string.forward_success),
if (!keyGuard.inKeyguardRestrictedInputMode) { getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
}
} else if (action == Intent.ACTION_USER_PRESENT) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
}
}
} }
} else if (action == Intent.ACTION_USER_PRESENT) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
} }
}
registerReceiver(lockReceiver, screenFilter) registerReceiver(lockReceiver, screenFilter)
} }
@@ -550,7 +464,7 @@ class ShadowsocksNatService extends Service with BaseService {
changeState(State.CONNECTING) changeState(State.CONNECTING)
spawn { Future {
if (config.proxy == "198.199.101.152") { if (config.proxy == "198.199.101.152") {
val holder = application.containerHolder val holder = application.containerHolder
@@ -570,8 +484,7 @@ class ShadowsocksNatService extends Service with BaseService {
killProcesses() killProcesses()
var resolved: Boolean = false var resolved: Boolean = false
if (!InetAddressUtils.isIPv4Address(config.proxy) && if (!Utils.isNumeric(config.proxy)) {
!InetAddressUtils.isIPv6Address(config.proxy)) {
Utils.resolve(config.proxy, enableIPv6 = true) match { Utils.resolve(config.proxy, enableIPv6 = true) match {
case Some(a) => case Some(a) =>
config.proxy = a config.proxy = a
@@ -631,7 +544,7 @@ class ShadowsocksNatService extends Service with BaseService {
stopSelf() stopSelf()
} }
stopForegroundCompat(1) stopForeground(true)
// change the state // change the state
changeState(State.STOPPED) changeState(State.STOPPED)
@@ -57,11 +57,7 @@ class ShadowsocksRunnerActivity extends Activity {
val connection = new ServiceConnection { val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) { override def onServiceConnected(name: ComponentName, service: IBinder) {
bgService = IShadowsocksService.Stub.asInterface(service) bgService = IShadowsocksService.Stub.asInterface(service)
handler.postDelayed(new Runnable() { handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
override def run() {
if (bgService != null) startBackgroundService()
}
}, 1000)
} }
override def onServiceDisconnected(name: ComponentName) { override def onServiceDisconnected(name: ComponentName) {
bgService = null bgService = null
@@ -122,11 +118,9 @@ class ShadowsocksRunnerActivity extends Activity {
val locked = km.inKeyguardRestrictedInputMode val locked = km.inKeyguardRestrictedInputMode
if (locked) { if (locked) {
val filter = new IntentFilter(Intent.ACTION_USER_PRESENT) val filter = new IntentFilter(Intent.ACTION_USER_PRESENT)
receiver = new BroadcastReceiver() { receiver = (context: Context, intent: Intent) => {
override def onReceive(context: Context, intent: Intent) { if (intent.getAction == Intent.ACTION_USER_PRESENT) {
if (intent.getAction == Intent.ACTION_USER_PRESENT) { attachService()
attachService()
}
} }
} }
registerReceiver(receiver, filter) registerReceiver(receiver, filter)
@@ -47,17 +47,18 @@ import android.content._
import android.content.pm.{PackageInfo, PackageManager} import android.content.pm.{PackageInfo, PackageManager}
import android.net.VpnService import android.net.VpnService
import android.os._ import android.os._
import android.support.v4.app.NotificationCompat
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders import com.google.android.gms.analytics.HitBuilders
import org.apache.commons.net.util.SubnetUtils import org.apache.commons.net.util.SubnetUtils
import org.apache.http.conn.util.InetAddressUtils
import scala.collection.mutable import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ops._ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class ShadowsocksVpnService extends VpnService with BaseService { class ShadowsocksVpnService extends VpnService with BaseService {
@@ -65,8 +66,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val TAG = "ShadowsocksVpnService" val TAG = "ShadowsocksVpnService"
val VPN_MTU = 1500 val VPN_MTU = 1500
val PRIVATE_VLAN = "26.26.26.%s" val PRIVATE_VLAN = "26.26.26.%s"
val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
var conn: ParcelFileDescriptor = null var conn: ParcelFileDescriptor = null
var notificationManager: NotificationManager = null
var receiver: BroadcastReceiver = null var receiver: BroadcastReceiver = null
var apps: Array[ProxiedApp] = null var apps: Array[ProxiedApp] = null
var config: Config = null var config: Config = null
@@ -103,15 +104,34 @@ class ShadowsocksVpnService extends VpnService with BaseService {
null null
} }
def notifyForegroundAlert(title: String, info: String, visible: Boolean) {
val openIntent = new Intent(this, classOf[Shadowsocks])
val contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0)
val closeIntent = new Intent(Action.CLOSE)
val actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0)
val builder = new NotificationCompat.Builder(this)
builder
.setWhen(0)
.setTicker(title)
.setContentTitle(getString(R.string.app_name))
.setContentText(info)
.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop),
actionIntent)
if (visible)
builder.setPriority(NotificationCompat.PRIORITY_DEFAULT)
else
builder.setPriority(NotificationCompat.PRIORITY_MIN)
startForeground(1, builder.build)
}
override def onCreate() { override def onCreate() {
super.onCreate() super.onCreate()
ConfigUtils.refresh(this) ConfigUtils.refresh(this)
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
.asInstanceOf[NotificationManager]
} }
override def onRevoke() { override def onRevoke() {
@@ -125,6 +145,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
vpnThread = null vpnThread = null
} }
stopForeground(true)
// channge the state // channge the state
changeState(State.STOPPING) changeState(State.STOPPING)
@@ -207,17 +229,15 @@ class ShadowsocksVpnService extends VpnService with BaseService {
// register close receiver // register close receiver
val filter = new IntentFilter() val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Intent.ACTION_SHUTDOWN)
receiver = new BroadcastReceiver { receiver = (p1: Context, p2: Intent) => {
def onReceive(p1: Context, p2: Intent) { Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT)
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT) stopRunner()
stopRunner()
}
} }
registerReceiver(receiver, filter) registerReceiver(receiver, filter)
changeState(State.CONNECTING) changeState(State.CONNECTING)
spawn { Future {
if (config.proxy == "198.199.101.152") { if (config.proxy == "198.199.101.152") {
val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder
try { try {
@@ -237,8 +257,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
// Resolve the server address // Resolve the server address
var resolved: Boolean = false var resolved: Boolean = false
if (!InetAddressUtils.isIPv4Address(config.proxy) && if (!Utils.isNumeric(config.proxy)) {
!InetAddressUtils.isIPv6Address(config.proxy)) {
Utils.resolve(config.proxy, enableIPv6 = true) match { Utils.resolve(config.proxy, enableIPv6 = true) match {
case Some(addr) => case Some(addr) =>
config.proxy = addr config.proxy = addr
@@ -250,6 +269,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
} }
if (resolved && handleConnection) { if (resolved && handleConnection) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
changeState(State.CONNECTED) changeState(State.CONNECTED)
} else { } else {
changeState(State.STOPPED, getString(R.string.service_failed)) changeState(State.STOPPED, getString(R.string.service_failed))
@@ -302,12 +323,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}) })
val cmd = new ArrayBuffer[String] val cmd = new ArrayBuffer[String]
cmd +=(Path.BASE + "ss-local", "-V", "-u" cmd += (Path.BASE + "ss-local", "-V", "-u"
, "-b", "127.0.0.1" , "-b", "127.0.0.1"
, "-t", "600" , "-t", "600"
, "-c", Path.BASE + "ss-local-vpn.conf" , "-c", Path.BASE + "ss-local-vpn.conf"
, "-f", Path.BASE + "ss-local-vpn.pid") , "-f", Path.BASE + "ss-local-vpn.pid")
if (config.isAuth) cmd += "-A"
if (config.route != Route.ALL) { if (config.route != Route.ALL) {
cmd += "--acl" cmd += "--acl"
cmd += (Path.BASE + "acl.list") cmd += (Path.BASE + "acl.list")
@@ -325,7 +348,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
p.println(conf) p.println(conf)
}) })
val cmd = new ArrayBuffer[String] val cmd = new ArrayBuffer[String]
cmd +=(Path.BASE + "ss-tunnel" cmd += (Path.BASE + "ss-tunnel"
, "-V" , "-V"
, "-u" , "-u"
, "-t", "10" , "-t", "10"
@@ -335,20 +358,23 @@ class ShadowsocksVpnService extends VpnService with BaseService {
, "-c", Path.BASE + "ss-tunnel-vpn.conf" , "-c", Path.BASE + "ss-tunnel-vpn.conf"
, "-f", Path.BASE + "ss-tunnel-vpn.pid") , "-f", Path.BASE + "ss-tunnel-vpn.pid")
if (config.isAuth) cmd += "-A"
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
Console.runCommand(cmd.mkString(" ")) Console.runCommand(cmd.mkString(" "))
} }
def startDnsDaemon() { def startDnsDaemon() {
val ipv6 = if (config.isIpv6) "" else "reject = ::/0;"
val conf = { val conf = {
if (config.route == Route.BYPASS_CHN) { if (config.route == Route.BYPASS_CHN) {
val reject = ConfigUtils.getRejectList(getContext, application) val reject = ConfigUtils.getRejectList(getContext, application)
val blackList = ConfigUtils.getBlackList(getContext, application) val blackList = ConfigUtils.getBlackList(getContext, application)
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163) Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163, ipv6)
} else { } else {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153, ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
Path.BASE + "pdnsd-vpn.pid", 8163) Path.BASE + "pdnsd-vpn.pid", 8163, ipv6)
} }
} }
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => {
@@ -371,9 +397,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24) .addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
.addDnsServer("8.8.8.8") .addDnsServer("8.8.8.8")
if (Utils.isLollipopOrAbove) { if (config.isIpv6) {
builder.addAddress(PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "1"), 126)
builder.addRoute("::", 0)
}
builder.allowFamily(android.system.OsConstants.AF_INET6) if (Utils.isLollipopOrAbove) {
if (!config.isGlobalProxy) { if (!config.isGlobalProxy) {
val apps = AppManager.getProxiedApps(this, config.proxiedAppString) val apps = AppManager.getProxiedApps(this, config.proxiedAppString)
@@ -429,7 +458,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
+ "--tunmtu %d " + "--tunmtu %d "
+ "--loglevel 3 " + "--loglevel 3 "
+ "--pid %stun2socks-vpn.pid") + "--pid %stun2socks-vpn.pid")
.formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"), config.localPort, fd, VPN_MTU, Path.BASE) .formatLocal(Locale.ENGLISH,
PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"),
config.localPort, fd, VPN_MTU, Path.BASE)
if (config.isIpv6)
cmd += " --netif-ip6addr " + PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "2")
if (config.isUdpDns) if (config.isUdpDns)
cmd += " --enable-udprelay" cmd += " --enable-udprelay"
@@ -440,7 +474,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
Console.runCommand(cmd) Console.runCommand(cmd)
return fd fd
} }
override def stopBackgroundService() { override def stopBackgroundService() {
@@ -93,35 +93,34 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
try { try {
val socket = serverSocket.accept() val socket = serverSocket.accept()
pool.execute(new Runnable { pool.execute(() => {
override def run() { try {
try { val input = socket.getInputStream
val input = socket.getInputStream val output = socket.getOutputStream
val output = socket.getOutputStream
input.read() input.read()
val fds = socket.getAncillaryFileDescriptors val fds = socket.getAncillaryFileDescriptors
if (fds.length > 0) { if (fds.nonEmpty) {
var ret = false var ret = false
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$") val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
val fd = getInt.invoke(fds(0)).asInstanceOf[Int] val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
ret = vpnService.protect(fd) ret = vpnService.protect(fd)
// Trick to close file decriptor // Trick to close file decriptor
System.jniclose(fd) System.jniclose(fd)
if (ret) { if (ret) {
output.write(0) output.write(0)
} else { } else {
output.write(1) output.write(1)
}
input.close()
output.close()
} }
input.close()
output.close()
}
} catch { } catch {
case e: Exception => case e: Exception =>
Log.e(TAG, "Error when protect socket", e) Log.e(TAG, "Error when protect socket", e)
@@ -134,12 +133,11 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
case _: Exception => // ignore case _: Exception => // ignore
} }
}}) })
} catch { } catch {
case e: IOException => { case e: IOException =>
Log.e(TAG, "Error when accept socket", e) Log.e(TAG, "Error when accept socket", e)
return return
}
} }
} }
} }
@@ -51,7 +51,7 @@ object DBHelper {
} }
class DBHelper(val context: Context) class DBHelper(val context: Context)
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 9) { extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 11) {
lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile]) lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
@@ -62,14 +62,23 @@ class DBHelper(val context: Context)
def onUpgrade(database: SQLiteDatabase, connectionSource: ConnectionSource, oldVersion: Int, def onUpgrade(database: SQLiteDatabase, connectionSource: ConnectionSource, oldVersion: Int,
newVersion: Int) { newVersion: Int) {
if (oldVersion != newVersion) { if (oldVersion != newVersion) {
if (oldVersion < 8) { if (oldVersion < 7) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
} else if (oldVersion < 9) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
} else {
profileDao.executeRaw("DROP TABLE IF EXISTS 'profile';") profileDao.executeRaw("DROP TABLE IF EXISTS 'profile';")
onCreate(database, connectionSource) onCreate(database, connectionSource)
} else {
if (oldVersion < 8) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 9) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 10) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
}
if (oldVersion < 11) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
} }
} }
} }
@@ -40,14 +40,10 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import android.content.Context import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.TextView
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import com.github.shadowsocks.{R} import android.view.{LayoutInflater, View, ViewGroup}
import android.view.View.{OnLongClickListener, OnClickListener} import android.widget.{BaseAdapter, TextView}
import com.github.shadowsocks.R
abstract class Item { abstract class Item {
def id: Int def id: Int
@@ -147,17 +143,13 @@ class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter {
} }
} }
tv.setOnClickListener(new OnClickListener { tv.setOnClickListener((view: View) => {
def onClick(view: View) { val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
val item = view.getTag(R.id.mdItem).asInstanceOf[Item] item.click(item.id)
item.click(item.id)
}
}) })
tv.setOnLongClickListener(new OnLongClickListener { tv.setOnLongClickListener((view: View) => {
def onLongClick(view: View): Boolean = { val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
val item = view.getTag(R.id.mdItem).asInstanceOf[Item] item.longClick(item.id)
item.longClick(item.id)
}
}) })
v.setTag(R.id.mdItem, value) v.setTag(R.id.mdItem, value)
@@ -90,6 +90,12 @@ class Profile {
@DatabaseField @DatabaseField
var udpdns: Boolean = false var udpdns: Boolean = false
@DatabaseField
var auth: Boolean = false
@DatabaseField
var ipv6: Boolean = false
@DatabaseField(dataType = DataType.LONG_STRING) @DatabaseField(dataType = DataType.LONG_STRING)
var individual: String = "" var individual: String = ""
@@ -39,9 +39,9 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import android.content.SharedPreferences
import android.util.Log import android.util.Log
import com.github.shadowsocks._ import com.github.shadowsocks._
import android.content.{SharedPreferences, Context}
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) { class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
@@ -122,6 +122,8 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
edit.putBoolean(Key.isBypassApps, profile.bypass) edit.putBoolean(Key.isBypassApps, profile.bypass)
edit.putBoolean(Key.isTrafficStat, profile.traffic) edit.putBoolean(Key.isTrafficStat, profile.traffic)
edit.putBoolean(Key.isUdpDns, profile.udpdns) edit.putBoolean(Key.isUdpDns, profile.udpdns)
edit.putBoolean(Key.isAuth, profile.auth)
edit.putBoolean(Key.isIpv6, profile.ipv6)
edit.putString(Key.profileName, profile.name) edit.putString(Key.profileName, profile.name)
edit.putString(Key.proxy, profile.host) edit.putString(Key.proxy, profile.host)
edit.putString(Key.sitekey, profile.password) edit.putString(Key.sitekey, profile.password)
@@ -146,6 +148,8 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
profile.bypass = settings.getBoolean(Key.isBypassApps, false) profile.bypass = settings.getBoolean(Key.isBypassApps, false)
profile.traffic = settings.getBoolean(Key.isTrafficStat, false) profile.traffic = settings.getBoolean(Key.isTrafficStat, false)
profile.udpdns = settings.getBoolean(Key.isUdpDns, false) profile.udpdns = settings.getBoolean(Key.isUdpDns, false)
profile.auth = settings.getBoolean(Key.isAuth, false)
profile.ipv6 = settings.getBoolean(Key.isIpv6, false)
profile.name = settings.getString(Key.profileName, "default") profile.name = settings.getString(Key.profileName, "default")
profile.host = settings.getString(Key.proxy, "127.0.0.1") profile.host = settings.getString(Key.proxy, "127.0.0.1")
profile.password = settings.getString(Key.sitekey, "default") profile.password = settings.getString(Key.sitekey, "default")
@@ -82,7 +82,7 @@ object ConfigUtils {
| label = "local"; | label = "local";
| ip = 127.0.0.1; | ip = 127.0.0.1;
| port = %d; | port = %d;
| reject = ::/0; | %s
| reject_policy = negate; | reject_policy = negate;
| reject_recursively = on; | reject_recursively = on;
| timeout = 5; | timeout = 5;
@@ -174,7 +174,9 @@ object ConfigUtils {
| label = "local-server"; | label = "local-server";
| ip = 127.0.0.1; | ip = 127.0.0.1;
| port = %d; | port = %d;
| timeout = 3; | %s
| reject_policy = negate;
| reject_recursively = on;
|} |}
| |
|rr { |rr {
@@ -231,8 +233,8 @@ object ConfigUtils {
.readTimeout(2000) .readTimeout(2000)
.send("sig="+sig) .send("sig="+sig)
.body .body
val proxies = util.Random.shuffle(list.split('|').toSeq).toSeq val proxies = util.Random.shuffle(list.split('|').toSeq)
val proxy = proxies(0).split(':') val proxy = proxies.head.split(':')
val host = proxy(0).trim val host = proxy(0).trim
val port = proxy(1).trim.toInt val port = proxy(1).trim.toInt
@@ -240,7 +242,7 @@ object ConfigUtils {
val method = proxy(3).trim val method = proxy(3).trim
new Config(config.isGlobalProxy, config.isGFWList, config.isBypassApps, config.isTrafficStat, new Config(config.isGlobalProxy, config.isGFWList, config.isBypassApps, config.isTrafficStat,
config.isUdpDns, config.profileName, host, password, method, config.proxiedAppString, config.route, port, config.isUdpDns, config.isAuth, config.isIpv6, config.profileName, host, password, method, config.proxiedAppString, config.route, port,
config.localPort) config.localPort)
} }
@@ -250,6 +252,8 @@ object ConfigUtils {
val isBypassApps = settings.getBoolean(Key.isBypassApps, false) val isBypassApps = settings.getBoolean(Key.isBypassApps, false)
val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false) val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false)
val isUdpDns = settings.getBoolean(Key.isUdpDns, false) val isUdpDns = settings.getBoolean(Key.isUdpDns, false)
val isAuth = settings.getBoolean(Key.isAuth, false)
val isIpv6 = settings.getBoolean(Key.isIpv6, false)
val profileName = settings.getString(Key.profileName, "default") val profileName = settings.getString(Key.profileName, "default")
val proxy = settings.getString(Key.proxy, "127.0.0.1") val proxy = settings.getString(Key.proxy, "127.0.0.1")
@@ -271,7 +275,7 @@ object ConfigUtils {
} }
val proxiedAppString = settings.getString(Key.proxied, "") val proxiedAppString = settings.getString(Key.proxied, "")
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, isUdpDns, profileName, proxy, new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, isUdpDns, isAuth, isIpv6, profileName, proxy,
sitekey, encMethod, proxiedAppString, route, remotePort, localPort) sitekey, encMethod, proxiedAppString, route, remotePort, localPort)
} }
} }
@@ -39,10 +39,11 @@
package com.github.shadowsocks.utils package com.github.shadowsocks.utils
import eu.chainfire.libsuperuser.Shell
import eu.chainfire.libsuperuser.Shell.{OnCommandResultListener, SU, Builder}
import java.util import java.util
import eu.chainfire.libsuperuser.Shell
import eu.chainfire.libsuperuser.Shell.{Builder, SU}
object Console { object Console {
private def openShell(): Shell.Interactive = { private def openShell(): Shell.Interactive = {
@@ -68,14 +69,8 @@ object Console {
def runCommand(commands: Array[String]) { def runCommand(commands: Array[String]) {
val shell = openShell() val shell = openShell()
shell.addCommand(commands, 0, new OnCommandResultListener { shell.addCommand(commands, 0, ((commandCode: Int, exitCode: Int, output: util.List[String]) =>
override def onCommandResult(commandCode: Int, exitCode: Int, if (exitCode < 0) shell.close()): Shell.OnCommandResultListener)
output: util.List[String]) {
if (exitCode < 0) {
shell.close()
}
}
})
shell.waitForIdle() shell.waitForIdle()
shell.close() shell.close()
} }
@@ -90,17 +85,14 @@ object Console {
} }
val shell = openRootShell(context) val shell = openRootShell(context)
val sb = new StringBuilder val sb = new StringBuilder
shell.addCommand(commands, 0, new OnCommandResultListener { shell.addCommand(commands, 0, ((commandCode: Int, exitCode: Int, output: util.List[String]) => {
override def onCommandResult(commandCode: Int, exitCode: Int, if (exitCode < 0) {
output: util.List[String]) { shell.close()
if (exitCode < 0) { } else {
shell.close() import scala.collection.JavaConversions._
} else { output.foreach(line => sb.append(line).append('\n'))
import scala.collection.JavaConversions._
output.foreach(line => sb.append(line).append('\n'))
}
} }
}) }): Shell.OnCommandResultListener)
if (shell.waitForIdle()) { if (shell.waitForIdle()) {
shell.close() shell.close()
sb.toString() sb.toString()
@@ -79,6 +79,8 @@ object Key {
val isBypassApps = "isBypassApps" val isBypassApps = "isBypassApps"
val isTrafficStat = "isTrafficStat" val isTrafficStat = "isTrafficStat"
val isUdpDns = "isUdpDns" val isUdpDns = "isUdpDns"
val isAuth= "isAuth"
val isIpv6= "isIpv6"
val proxy = "proxy" val proxy = "proxy"
val sitekey = "sitekey" val sitekey = "sitekey"
@@ -47,11 +47,8 @@ import android.util.{Log, Base64}
object Parser { object Parser {
val TAG = "ShadowParser" val TAG = "ShadowParser"
def generate (profile: Profile): String = { def generate (profile: Profile): String = "ss://" + Base64.encodeToString("%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
val path = "%s:%s@%s:%d".formatLocal(Locale.ENGLISH, profile.method, profile.password, profile.host, profile.remotePort).getBytes, Base64.NO_PADDING)
profile.method, profile.password, profile.host, profile.remotePort)
return "ss://" + Base64.encodeToString(path.getBytes, Base64.NO_PADDING)
}
def parse (data: String): Option[Profile] = { def parse (data: String): Option[Profile] = {
try { try {
@@ -38,22 +38,21 @@
*/ */
package com.github.shadowsocks.utils package com.github.shadowsocks.utils
import android.content.{Intent, Context}
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.{BitmapDrawable, Drawable}
import android.util.{Base64, Log}
import java.io._ import java.io._
import java.net.{UnknownHostException, InetAddress, NetworkInterface} import java.net._
import org.apache.http.conn.util.InetAddressUtils import java.security.MessageDigest
import org.xbill.DNS._
import android.graphics._
import android.app.ActivityManager import android.app.ActivityManager
import android.content.pm.{ApplicationInfo, PackageManager}
import android.content.{Context, Intent}
import android.graphics._
import android.graphics.drawable.{BitmapDrawable, Drawable}
import android.os.Build import android.os.Build
import android.provider.Settings import android.provider.Settings
import scala.Some import android.support.v4.content.ContextCompat
import java.security.MessageDigest import android.util.{Base64, Log}
import com.github.shadowsocks.{BuildConfig} import com.github.shadowsocks.BuildConfig
import org.xbill.DNS._
object Utils { object Utils {
@@ -147,6 +146,7 @@ object Utils {
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n") // + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n")
} }
//noinspection ScalaDeprecation
private def toggleBelowApiLevel17(context: Context) { private def toggleBelowApiLevel17(context: Context) {
// Android 4.2 below // Android 4.2 below
Settings.System.putInt(context.getContentResolver, Settings.System.AIRPLANE_MODE_ON, 1) Settings.System.putInt(context.getContentResolver, Settings.System.AIRPLANE_MODE_ON, 1)
@@ -198,7 +198,7 @@ object Utils {
} }
} }
} catch { } catch {
case e: Exception => None case e: Exception =>
} }
None None
} }
@@ -233,6 +233,9 @@ object Utils {
None None
} }
private lazy val isNumericMethod = classOf[InetAddress].getMethod("isNumeric", classOf[String])
def isNumeric(address: String): Boolean = isNumericMethod.invoke(null, address).asInstanceOf[Boolean]
/** /**
* Get local IPv4 address * Get local IPv4 address
*/ */
@@ -245,9 +248,8 @@ object Utils {
while (addrs.hasMoreElements) { while (addrs.hasMoreElements) {
val addr = addrs.nextElement() val addr = addrs.nextElement()
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) { if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
val sAddr = addr.getHostAddress.toUpperCase if (addr.isInstanceOf[Inet4Address]) {
if (InetAddressUtils.isIPv4Address(sAddr)) { return Some(addr.getHostAddress.toUpperCase)
return Some(sAddr)
} }
} }
} }
@@ -271,8 +273,7 @@ object Utils {
while (addrs.hasMoreElements) { while (addrs.hasMoreElements) {
val addr = addrs.nextElement() val addr = addrs.nextElement()
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) { if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
val sAddr = addr.getHostAddress.toUpperCase if (addr.isInstanceOf[Inet6Address]) {
if (InetAddressUtils.isIPv6Address(sAddr)) {
if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected") if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected")
return true return true
} }
@@ -338,7 +339,7 @@ object Utils {
def getAppIcon(c: Context, packageName: String): Drawable = { def getAppIcon(c: Context, packageName: String): Drawable = {
val pm: PackageManager = c.getPackageManager val pm: PackageManager = c.getPackageManager
val icon: Drawable = c.getResources.getDrawable(android.R.drawable.sym_def_app_icon) val icon: Drawable = ContextCompat.getDrawable(c, android.R.drawable.sym_def_app_icon)
try { try {
pm.getApplicationIcon(packageName) pm.getApplicationIcon(packageName)
} catch { } catch {
@@ -348,7 +349,7 @@ object Utils {
def getAppIcon(c: Context, uid: Int): Drawable = { def getAppIcon(c: Context, uid: Int): Drawable = {
val pm: PackageManager = c.getPackageManager val pm: PackageManager = c.getPackageManager
val icon: Drawable = c.getResources.getDrawable(android.R.drawable.sym_def_app_icon) val icon: Drawable = ContextCompat.getDrawable(c, android.R.drawable.sym_def_app_icon)
val packages: Array[String] = pm.getPackagesForUid(uid) val packages: Array[String] = pm.getPackagesForUid(uid)
if (packages != null) { if (packages != null) {
if (packages.length >= 1) { if (packages.length >= 1) {