Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7098e57893 | ||
|
|
a4c5fc4919 | ||
|
|
3c69b23a5b | ||
|
|
738febbac7 | ||
|
|
ed91010b18 | ||
|
|
8f4d7db797 | ||
|
|
52ead0a02c | ||
|
|
7d099b1faf | ||
|
|
89b9914f15 | ||
|
|
809b81a332 | ||
|
|
395c07a0f6 | ||
|
|
cb61c290a4 | ||
|
|
9ccf2bc436 | ||
|
|
674d493c2d | ||
|
|
17382a1002 | ||
|
|
7370b6a0b8 | ||
|
|
158afb1d9f | ||
|
|
085c6dbebf | ||
|
|
c401d7eb3a | ||
|
|
118cea9f7a | ||
|
|
5b1ae13faa | ||
|
|
b5f655beb5 | ||
|
|
9a3ed94fe0 | ||
|
|
0191d1d0fc | ||
|
|
6a56704f52 | ||
|
|
a95308d283 | ||
|
|
6acbae0c7e | ||
|
|
97d31cb927 | ||
|
|
7474269f6b | ||
|
|
40447ce1d2 | ||
|
|
4162057a22 | ||
|
|
0f24d7c0bc | ||
|
|
bbf614484d | ||
|
|
396d31a0a7 | ||
|
|
812dc6769a | ||
|
|
8feb587ebe |
@@ -5,6 +5,7 @@ Please read FAQ in wiki then answer these questions before submitting your issue
|
||||
* Android version: *e.g. "7.0.0_r14", more detailed description is preferred*
|
||||
* Device: *e.g. Google Pixel XL*
|
||||
* Shadowsocks version: *version code "v3.0.0" or commit ID "a073f85"*
|
||||
* Last version that did not exhibit the issue (if applicable):
|
||||
|
||||
### Configuration
|
||||
|
||||
@@ -17,7 +18,13 @@ _Put an `x` inside the [ ] that applies._
|
||||
* Local port: 1080
|
||||
* Encrypt method:
|
||||
* [ ] One-time authentication
|
||||
* Route:
|
||||
* Route
|
||||
* [ ] All
|
||||
* [ ] Bypass LAN
|
||||
* [ ] Bypass China
|
||||
* [ ] Bypass LAN & China
|
||||
* [ ] GFW List
|
||||
* [ ] China List
|
||||
* [ ] IPv6 route
|
||||
* [ ] Per-App Proxy
|
||||
* [ ] Bypass mode
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ cache:
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- build-tools-24.0.1
|
||||
- build-tools-24.0.3
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
|
||||
|
||||
@@ -1,64 +1,55 @@
|
||||
import android.Keys._
|
||||
|
||||
android.Plugin.androidBuild
|
||||
|
||||
platformTarget in Android := "android-24"
|
||||
|
||||
name := "shadowsocks"
|
||||
|
||||
scalaVersion := "2.11.8"
|
||||
dexMaxHeap := "4g"
|
||||
|
||||
compileOrder in Compile := CompileOrder.JavaThenScala
|
||||
|
||||
javacOptions ++= Seq("-source", "1.7", "-target", "1.7")
|
||||
|
||||
scalacOptions ++= Seq("-target:jvm-1.7", "-Xexperimental")
|
||||
|
||||
ndkJavah in Android := List()
|
||||
|
||||
ndkBuild in Android := List()
|
||||
|
||||
shrinkResources in Android := true
|
||||
|
||||
typedResources in Android := false
|
||||
|
||||
resConfigs in Android := Seq("ru", "zh", "zh-rCN")
|
||||
|
||||
dexMaxHeap in Android := "4g"
|
||||
|
||||
resolvers += Resolver.jcenterRepo
|
||||
|
||||
enablePlugins(AndroidApp)
|
||||
useSupportVectors
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"com.android.support" % "design" % "24.2.1",
|
||||
"com.android.support" % "gridlayout-v7" % "24.2.1",
|
||||
"com.android.support" % "preference-v14" % "24.2.1",
|
||||
"com.github.clans" % "fab" % "1.6.4",
|
||||
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
|
||||
"com.github.kevinsawicki" % "http-request" % "6.0",
|
||||
"com.google.android.gms" % "play-services-ads" % "9.6.1",
|
||||
"com.google.android.gms" % "play-services-analytics" % "9.6.1",
|
||||
"com.j256.ormlite" % "ormlite-android" % "5.0",
|
||||
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2",
|
||||
"dnsjava" % "dnsjava" % "2.1.7",
|
||||
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809",
|
||||
"me.dm7.barcodescanner" % "zxing" % "1.9",
|
||||
"net.glxn.qrgen" % "android" % "2.0"
|
||||
)
|
||||
name := "shadowsocks"
|
||||
version := "3.2.7"
|
||||
versionCode := Some(167)
|
||||
|
||||
proguardVersion in Android := "5.3"
|
||||
platformTarget := "android-24"
|
||||
|
||||
proguardCache in Android := Seq()
|
||||
compileOrder := CompileOrder.JavaThenScala
|
||||
javacOptions ++= "-source" :: "1.7" :: "-target" :: "1.7" :: Nil
|
||||
scalacOptions ++= "-target:jvm-1.7" :: "-Xexperimental" :: Nil
|
||||
ndkJavah := Seq()
|
||||
ndkBuild := Seq()
|
||||
|
||||
proguardOptions in Android ++= Seq(
|
||||
"-keep class com.github.shadowsocks.System { *; }",
|
||||
"-dontwarn com.google.android.gms.internal.**",
|
||||
"-dontwarn com.j256.ormlite.**",
|
||||
"-dontwarn org.xbill.**")
|
||||
proguardVersion := "5.3"
|
||||
proguardCache := Seq()
|
||||
proguardOptions ++=
|
||||
"-keep class com.github.shadowsocks.System { *; }" ::
|
||||
"-dontwarn com.google.android.gms.internal.**" ::
|
||||
"-dontwarn com.j256.ormlite.**" ::
|
||||
"-dontwarn org.xbill.**" ::
|
||||
Nil
|
||||
|
||||
shrinkResources := true
|
||||
typedResources := false
|
||||
resConfigs := Seq("ru", "zh-rCN", "zh-rTW")
|
||||
|
||||
resolvers += Resolver.jcenterRepo
|
||||
libraryDependencies ++=
|
||||
"com.android.support" % "design" % "24.2.1" ::
|
||||
"com.android.support" % "gridlayout-v7" % "24.2.1" ::
|
||||
"com.android.support" % "preference-v14" % "24.2.1" ::
|
||||
"com.evernote" % "android-job" % "1.1.0" ::
|
||||
"com.github.clans" % "fab" % "1.6.4" ::
|
||||
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" ::
|
||||
"com.github.kevinsawicki" % "http-request" % "6.0" ::
|
||||
"com.google.android.gms" % "play-services-ads" % "9.6.1" ::
|
||||
"com.google.android.gms" % "play-services-analytics" % "9.6.1" ::
|
||||
"com.google.android.gms" % "play-services-gcm" % "9.6.1" ::
|
||||
"com.j256.ormlite" % "ormlite-android" % "5.0" ::
|
||||
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
|
||||
"dnsjava" % "dnsjava" % "2.1.7" ::
|
||||
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
|
||||
"me.dm7.barcodescanner" % "zxing" % "1.9" ::
|
||||
"net.glxn.qrgen" % "android" % "2.0" ::
|
||||
Nil
|
||||
|
||||
lazy val nativeBuild = TaskKey[Unit]("native-build", "Build native executables")
|
||||
|
||||
nativeBuild := {
|
||||
val logger = streams.value.log
|
||||
Process("./build.sh") ! logger match {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.18")
|
||||
addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.0")
|
||||
|
||||
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks"
|
||||
android:versionCode="164"
|
||||
android:versionName="3.2.4">
|
||||
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
@@ -158,6 +156,18 @@
|
||||
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- android-job components -->
|
||||
<service android:name="com.evernote.android.job.v21.PlatformJobService"
|
||||
android:process=":bg"/>
|
||||
<service android:name="com.evernote.android.job.v14.PlatformAlarmService"
|
||||
android:process=":bg"/>
|
||||
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
|
||||
android:process=":bg"/>
|
||||
<receiver android:name="com.evernote.android.job.v14.PlatformAlarmReceiver"
|
||||
android:process=":bg"/>
|
||||
<receiver android:name="com.evernote.android.job.JobBootReceiver"
|
||||
android:process=":bg"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+3779
-3989
File diff suppressed because it is too large
Load Diff
Submodule src/main/jni/shadowsocks-libev updated: 248fdcb610...7029cfca3a
@@ -48,12 +48,20 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="private_route">
|
||||
<item>0.0.0.0/8</item>
|
||||
<item>10.0.0.0/8</item>
|
||||
<item>100.64.0.0/10</item>
|
||||
<item>127.0.0.0/8</item>
|
||||
<item>169.254.0.0/16</item>
|
||||
<item>172.16.0.0/12</item>
|
||||
<item>192.0.0.0/29</item>
|
||||
<item>192.0.2.0/24</item>
|
||||
<item>192.88.99.0/24</item>
|
||||
<item>192.168.0.0/16</item>
|
||||
<item>198.18.0.0/15</item>
|
||||
<item>198.51.100.0/24</item>
|
||||
<item>203.0.113.0/24</item>
|
||||
<item>224.0.0.0/3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="bypass_private_route">
|
||||
@@ -66,8 +74,47 @@
|
||||
<item>16.0.0.0/4</item>
|
||||
<item>32.0.0.0/3</item>
|
||||
<item>64.0.0.0/3</item>
|
||||
<item>96.0.0.0/4</item>
|
||||
<item>112.0.0.0/5</item>
|
||||
<item>96.0.0.0/6</item>
|
||||
<item>100.0.0.0/10</item>
|
||||
<item>100.128.0.0/9</item>
|
||||
<item>101.0.0.0/8</item>
|
||||
<item>102.0.0.0/7</item>
|
||||
<item>104.0.0.0/5</item>
|
||||
<item>112.0.0.0/10</item>
|
||||
<item>112.64.0.0/11</item>
|
||||
<item>112.96.0.0/12</item>
|
||||
<item>112.112.0.0/13</item>
|
||||
<item>112.120.0.0/14</item>
|
||||
<item>112.124.0.0/19</item>
|
||||
<item>112.124.32.0/21</item>
|
||||
<item>112.124.40.0/22</item>
|
||||
<item>112.124.44.0/23</item>
|
||||
<item>112.124.46.0/24</item>
|
||||
<item>112.124.48.0/20</item>
|
||||
<item>112.124.64.0/18</item>
|
||||
<item>112.124.128.0/17</item>
|
||||
<item>112.125.0.0/16</item>
|
||||
<item>112.126.0.0/15</item>
|
||||
<item>112.128.0.0/9</item>
|
||||
<item>113.0.0.0/8</item>
|
||||
<item>114.0.0.0/10</item>
|
||||
<item>114.64.0.0/11</item>
|
||||
<item>114.96.0.0/12</item>
|
||||
<item>114.112.0.0/15</item>
|
||||
<item>114.114.0.0/18</item>
|
||||
<item>114.114.64.0/19</item>
|
||||
<item>114.114.96.0/20</item>
|
||||
<item>114.114.112.0/23</item>
|
||||
<item>114.114.115.0/24</item>
|
||||
<item>114.114.116.0/22</item>
|
||||
<item>114.114.120.0/21</item>
|
||||
<item>114.114.128.0/17</item>
|
||||
<item>114.115.0.0/16</item>
|
||||
<item>114.116.0.0/14</item>
|
||||
<item>114.120.0.0/13</item>
|
||||
<item>114.128.0.0/9</item>
|
||||
<item>115.0.0.0/8</item>
|
||||
<item>116.0.0.0/6</item>
|
||||
<item>120.0.0.0/6</item>
|
||||
<item>124.0.0.0/7</item>
|
||||
<item>126.0.0.0/8</item>
|
||||
@@ -109,12 +156,69 @@
|
||||
<item>192.8.0.0/13</item>
|
||||
<item>192.16.0.0/12</item>
|
||||
<item>192.32.0.0/11</item>
|
||||
<item>192.64.0.0/10</item>
|
||||
<item>192.128.0.0/9</item>
|
||||
<item>192.64.0.0/12</item>
|
||||
<item>192.80.0.0/13</item>
|
||||
<item>192.88.0.0/18</item>
|
||||
<item>192.88.64.0/19</item>
|
||||
<item>192.88.96.0/23</item>
|
||||
<item>192.88.98.0/24</item>
|
||||
<item>192.88.100.0/22</item>
|
||||
<item>192.88.104.0/21</item>
|
||||
<item>192.88.112.0/20</item>
|
||||
<item>192.88.128.0/17</item>
|
||||
<item>192.89.0.0/16</item>
|
||||
<item>192.90.0.0/15</item>
|
||||
<item>192.92.0.0/14</item>
|
||||
<item>192.96.0.0/11</item>
|
||||
<item>192.128.0.0/11</item>
|
||||
<item>192.160.0.0/13</item>
|
||||
<item>192.169.0.0/16</item>
|
||||
<item>192.170.0.0/15</item>
|
||||
<item>192.172.0.0/14</item>
|
||||
<item>192.176.0.0/12</item>
|
||||
<item>192.192.0.0/10</item>
|
||||
<item>193.0.0.0/8</item>
|
||||
<item>194.0.0.0/7</item>
|
||||
<item>196.0.0.0/6</item>
|
||||
<item>200.0.0.0/5</item>
|
||||
<item>196.0.0.0/7</item>
|
||||
<item>198.0.0.0/12</item>
|
||||
<item>198.16.0.0/15</item>
|
||||
<item>198.20.0.0/14</item>
|
||||
<item>198.24.0.0/13</item>
|
||||
<item>198.32.0.0/12</item>
|
||||
<item>198.48.0.0/15</item>
|
||||
<item>198.50.0.0/16</item>
|
||||
<item>198.51.0.0/18</item>
|
||||
<item>198.51.64.0/19</item>
|
||||
<item>198.51.96.0/22</item>
|
||||
<item>198.51.101.0/24</item>
|
||||
<item>198.51.102.0/23</item>
|
||||
<item>198.51.104.0/21</item>
|
||||
<item>198.51.112.0/20</item>
|
||||
<item>198.51.128.0/17</item>
|
||||
<item>198.52.0.0/14</item>
|
||||
<item>198.56.0.0/13</item>
|
||||
<item>198.64.0.0/10</item>
|
||||
<item>198.128.0.0/9</item>
|
||||
<item>199.0.0.0/8</item>
|
||||
<item>200.0.0.0/7</item>
|
||||
<item>202.0.0.0/8</item>
|
||||
<item>203.0.0.0/18</item>
|
||||
<item>203.0.64.0/19</item>
|
||||
<item>203.0.96.0/20</item>
|
||||
<item>203.0.112.0/24</item>
|
||||
<item>203.0.114.0/23</item>
|
||||
<item>203.0.116.0/22</item>
|
||||
<item>203.0.120.0/21</item>
|
||||
<item>203.0.128.0/17</item>
|
||||
<item>203.1.0.0/16</item>
|
||||
<item>203.2.0.0/15</item>
|
||||
<item>203.4.0.0/14</item>
|
||||
<item>203.8.0.0/13</item>
|
||||
<item>203.16.0.0/12</item>
|
||||
<item>203.32.0.0/11</item>
|
||||
<item>203.64.0.0/10</item>
|
||||
<item>203.128.0.0/9</item>
|
||||
<item>204.0.0.0/6</item>
|
||||
<item>208.0.0.0/4</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
android:persistent="false"
|
||||
android:summary="%s"
|
||||
android:title="@string/sitekey"/>
|
||||
<android.support.v7.preference.DropDownPreference
|
||||
<DropDownPreference
|
||||
android:key="encMethod"
|
||||
android:persistent="false"
|
||||
android:entries="@array/enc_method_entry"
|
||||
@@ -53,7 +53,7 @@
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
android:title="@string/feature_cat">
|
||||
|
||||
<android.support.v7.preference.DropDownPreference
|
||||
<DropDownPreference
|
||||
android:key="route"
|
||||
android:persistent="false"
|
||||
android:entries="@array/route_entry"
|
||||
@@ -121,9 +121,6 @@
|
||||
android:summary="@string/nat_summary"/>
|
||||
<Preference android:key="recovery" android:title="@string/recovery"
|
||||
android:summary="@string/recovery_summary"/>
|
||||
<Preference android:key="flush_dnscache"
|
||||
android:title="@string/flush_dnscache"
|
||||
android:summary="@string/flush_dnscache_summary"/>
|
||||
<Preference android:key="about" android:title="@string/about"/>
|
||||
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
|
||||
@@ -2,14 +2,11 @@ package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v14.preference.PreferenceDialogFragment
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.view.{View, ViewGroup}
|
||||
|
||||
class NumberPickerPreferenceDialogFragment extends PreferenceDialogFragment {
|
||||
private lazy val preference = getPreference.asInstanceOf[NumberPickerPreference]
|
||||
private lazy val picker = preference.picker
|
||||
private lazy val inputMethodManager =
|
||||
getActivity.getSystemService(Context.INPUT_METHOD_SERVICE).asInstanceOf[InputMethodManager]
|
||||
|
||||
override protected def onCreateDialogView(context: Context) = {
|
||||
val parent = picker.getParent.asInstanceOf[ViewGroup]
|
||||
@@ -30,7 +27,5 @@ class NumberPickerPreferenceDialogFragment extends PreferenceDialogFragment {
|
||||
val value = picker.getValue
|
||||
if (preference.callChangeListener(value)) preference.setValue(value)
|
||||
}
|
||||
inputMethodManager.hideSoftInputFromWindow(getActivity.getCurrentFocus.getWindowToken,
|
||||
InputMethodManager.HIDE_NOT_ALWAYS)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ abstract class PreferenceFragment extends Base {
|
||||
bundle.putString("key", key)
|
||||
fragment.setArguments(bundle)
|
||||
fragment.setTargetFragment(this, 0)
|
||||
fragment.show(getFragmentManager, "android.support.v14.preference.PreferenceFragment.DIALOG")
|
||||
getFragmentManager.beginTransaction()
|
||||
.add(fragment, "android.support.v14.preference.PreferenceFragment.DIALOG")
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
override protected def onCreateAdapter(screen: PreferenceScreen) = new PreferenceGroupAdapter(screen)
|
||||
|
||||
@@ -50,10 +50,10 @@ import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.github.kevinsawicki.http.HttpRequest
|
||||
import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback}
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback}
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
trait BaseService extends Service {
|
||||
|
||||
@@ -62,6 +62,7 @@ trait BaseService extends Service {
|
||||
|
||||
case class NameNotResolvedException() extends IOException
|
||||
case class KcpcliParseException(cause: Throwable) extends Exception(cause)
|
||||
case class NullConnectionException() extends NullPointerException
|
||||
|
||||
var timer: Timer = _
|
||||
var trafficMonitorThread: TrafficMonitorThread = _
|
||||
@@ -141,8 +142,7 @@ trait BaseService extends Service {
|
||||
}
|
||||
|
||||
def checkProfile(profile: Profile) = if (TextUtils.isEmpty(profile.host) || TextUtils.isEmpty(profile.password)) {
|
||||
changeState(State.STOPPED, getString(R.string.proxy_empty))
|
||||
stopRunner(true)
|
||||
stopRunner(true, getString(R.string.proxy_empty))
|
||||
false
|
||||
} else true
|
||||
|
||||
@@ -200,6 +200,7 @@ trait BaseService extends Service {
|
||||
case _: NameNotResolvedException => stopRunner(true, getString(R.string.invalid_server))
|
||||
case exc: KcpcliParseException =>
|
||||
stopRunner(true, getString(R.string.service_failed) + ": " + exc.cause.getMessage)
|
||||
case _: NullConnectionException => stopRunner(true, getString(R.string.reboot_required))
|
||||
case exc: Throwable =>
|
||||
stopRunner(true, getString(R.string.service_failed) + ": " + exc.getMessage)
|
||||
exc.printStackTrace()
|
||||
@@ -277,6 +278,7 @@ trait BaseService extends Service {
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
app.refreshContainerHolder
|
||||
app.updateAssets()
|
||||
}
|
||||
|
||||
// Service of shadowsocks should always be started explicitly
|
||||
|
||||
@@ -38,17 +38,14 @@
|
||||
*/
|
||||
package com.github.shadowsocks
|
||||
|
||||
import java.io.{FileOutputStream, IOException, InputStream, OutputStream}
|
||||
import java.lang.System.currentTimeMillis
|
||||
import java.net.{HttpURLConnection, URL}
|
||||
import java.util.Date
|
||||
import java.util.Hashtable
|
||||
import java.util.Locale
|
||||
import java.util
|
||||
import java.util.{GregorianCalendar, Locale}
|
||||
|
||||
import android.app.backup.BackupManager
|
||||
import android.app.{Activity, ProgressDialog}
|
||||
import android.content._
|
||||
import android.content.res.AssetManager
|
||||
import android.graphics.Typeface
|
||||
import android.net.VpnService
|
||||
import android.os._
|
||||
@@ -66,9 +63,7 @@ import com.github.shadowsocks.database._
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.util.Random
|
||||
|
||||
object Typefaces {
|
||||
@@ -89,15 +84,13 @@ object Typefaces {
|
||||
}
|
||||
|
||||
private final val TAG = "Typefaces"
|
||||
private final val cache = new Hashtable[String, Typeface]
|
||||
private final val cache = new util.Hashtable[String, Typeface]
|
||||
}
|
||||
|
||||
object Shadowsocks {
|
||||
// Constants
|
||||
private final val TAG = "Shadowsocks"
|
||||
private final val REQUEST_CONNECT = 1
|
||||
private val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL,
|
||||
Executable.TUN2SOCKS, Executable.KCPTUN)
|
||||
}
|
||||
|
||||
class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
@@ -187,14 +180,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
snackbar.setAction(R.string.switch_to_vpn, (_ => preferences.natSwitch.setChecked(false)): View.OnClickListener)
|
||||
snackbar.show
|
||||
}
|
||||
|
||||
if (app.settings.getInt(Key.currentVersionCode, -1) != BuildConfig.VERSION_CODE) {
|
||||
app.editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
|
||||
|
||||
recovery()
|
||||
|
||||
updateCurrentProfile()
|
||||
}
|
||||
}
|
||||
|
||||
override def onServiceDisconnected() {
|
||||
@@ -202,8 +187,8 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
}
|
||||
|
||||
override def binderDied {
|
||||
detachService
|
||||
crashRecovery
|
||||
detachService()
|
||||
app.crashRecovery()
|
||||
attachService
|
||||
}
|
||||
|
||||
@@ -241,70 +226,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
}
|
||||
}
|
||||
|
||||
private def copyAssets(path: String) {
|
||||
val assetManager: AssetManager = getAssets
|
||||
var files: Array[String] = null
|
||||
try {
|
||||
files = assetManager.list(path)
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
Log.e(TAG, e.getMessage)
|
||||
app.track(e)
|
||||
}
|
||||
if (files != null) {
|
||||
for (file <- files) {
|
||||
var in: InputStream = null
|
||||
var out: OutputStream = null
|
||||
try {
|
||||
if (path.length > 0) {
|
||||
in = assetManager.open(path + "/" + file)
|
||||
} else {
|
||||
in = assetManager.open(file)
|
||||
}
|
||||
out = new FileOutputStream(getApplicationInfo.dataDir + "/" + file)
|
||||
copyFile(in, out)
|
||||
in.close()
|
||||
in = null
|
||||
out.flush()
|
||||
out.close()
|
||||
out = null
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
Log.e(TAG, ex.getMessage)
|
||||
app.track(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def copyFile(in: InputStream, out: OutputStream) {
|
||||
val buffer: Array[Byte] = new Array[Byte](1024)
|
||||
var read: Int = 0
|
||||
while ( {
|
||||
read = in.read(buffer)
|
||||
read
|
||||
} != -1) {
|
||||
out.write(buffer, 0, read)
|
||||
}
|
||||
}
|
||||
|
||||
def crashRecovery() {
|
||||
val cmd = new ArrayBuffer[String]()
|
||||
|
||||
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks", "kcptun")) {
|
||||
cmd.append("killall %s".formatLocal(Locale.ENGLISH, task))
|
||||
cmd.append("rm -f %1$s/%2$s-nat.conf %1$s/%2$s-vpn.conf"
|
||||
.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
|
||||
}
|
||||
if (app.isNatEnabled) {
|
||||
cmd.append("iptables -t nat -F OUTPUT")
|
||||
cmd.append("echo done")
|
||||
val result = Shell.SU.run(cmd.toArray)
|
||||
if (result != null && !result.isEmpty) return // fallback to SH
|
||||
}
|
||||
Shell.SH.run(cmd.toArray)
|
||||
}
|
||||
|
||||
def cancelStart() {
|
||||
clearDialog()
|
||||
changeSwitch(checked = false)
|
||||
@@ -497,7 +418,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
val year = 1975 + random.nextInt(40)
|
||||
val month = 1 + random.nextInt(12)
|
||||
val day = random.nextInt(28)
|
||||
adBuilder.setBirthday(new Date(year, month, day))
|
||||
adBuilder.setBirthday(new GregorianCalendar(year, month, day).getTime)
|
||||
|
||||
// Load Ad
|
||||
adView.loadAd(adBuilder.build())
|
||||
@@ -523,33 +444,11 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
def reset() {
|
||||
crashRecovery()
|
||||
|
||||
copyAssets(System.getABI)
|
||||
copyAssets("acl")
|
||||
|
||||
val ab = new ArrayBuffer[String]
|
||||
for (executable <- EXECUTABLES) {
|
||||
ab.append("chmod 755 " + getApplicationInfo.dataDir + "/" + executable)
|
||||
}
|
||||
Shell.SH.run(ab.toArray)
|
||||
}
|
||||
|
||||
def recovery() {
|
||||
if (serviceStarted) serviceStop()
|
||||
val h = showProgress(R.string.recovering)
|
||||
Utils.ThrowableFuture {
|
||||
reset()
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}
|
||||
|
||||
def flushDnsCache() {
|
||||
val h = showProgress(R.string.flushing)
|
||||
Utils.ThrowableFuture {
|
||||
if (!Utils.toggleAirplaneMode(getBaseContext)) h.post(() => Snackbar.make(findViewById(android.R.id.content),
|
||||
R.string.flush_dnscache_no_root, Snackbar.LENGTH_LONG).show)
|
||||
app.copyAssets()
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,21 +39,43 @@
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import java.io.{FileOutputStream, IOException}
|
||||
import java.util
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
import android.os.{Build, LocaleList}
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatDelegate
|
||||
import android.util.Log
|
||||
import com.evernote.android.job.JobManager
|
||||
import com.github.shadowsocks.database.{DBHelper, ProfileManager}
|
||||
import com.github.shadowsocks.utils.{Key, Utils, TcpFastOpen}
|
||||
import com.github.shadowsocks.job.DonaldTrump
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders, StandardExceptionParser}
|
||||
import com.google.android.gms.common.api.ResultCallback
|
||||
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
|
||||
import com.j256.ormlite.logger.LocalLog
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object ShadowsocksApplication {
|
||||
var app: ShadowsocksApplication = _
|
||||
|
||||
private final val TAG = "ShadowsocksApplication"
|
||||
private val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL,
|
||||
Executable.TUN2SOCKS, Executable.KCPTUN)
|
||||
|
||||
// The ones in Locale doesn't have script included
|
||||
private final lazy val SIMPLIFIED_CHINESE =
|
||||
if (Build.VERSION.SDK_INT >= 21) Locale.forLanguageTag("zh-Hans-CN") else Locale.SIMPLIFIED_CHINESE
|
||||
private final lazy val TRADITIONAL_CHINESE =
|
||||
if (Build.VERSION.SDK_INT >= 21) Locale.forLanguageTag("zh-Hant-TW") else Locale.TRADITIONAL_CHINESE
|
||||
}
|
||||
|
||||
class ShadowsocksApplication extends Application {
|
||||
@@ -88,10 +110,62 @@ class ShadowsocksApplication extends Application {
|
||||
profileManager.getProfile(id) getOrElse profileManager.createProfile()
|
||||
}
|
||||
|
||||
private def checkChineseLocale(locale: Locale): Locale = if (locale.getLanguage == "zh") locale.getCountry match {
|
||||
case "CN" | "TW" => null // already supported
|
||||
case _ => locale.getScript match { // fallback to the corresponding script
|
||||
case "Hans" => SIMPLIFIED_CHINESE
|
||||
case "Hant" => TRADITIONAL_CHINESE
|
||||
case script =>
|
||||
Log.w(TAG, "Unknown zh locale script: %s. Falling back to trying countries...".format(script))
|
||||
locale.getCountry match {
|
||||
case "SG" => SIMPLIFIED_CHINESE
|
||||
case "HK" | "MO" => TRADITIONAL_CHINESE
|
||||
case _ =>
|
||||
Log.w(TAG, "Unknown zh locale: %s. Falling back to zh-Hans-CN...".format(locale.toLanguageTag))
|
||||
SIMPLIFIED_CHINESE
|
||||
}
|
||||
}
|
||||
} else null
|
||||
|
||||
@SuppressLint(Array("NewApi"))
|
||||
private def checkChineseLocale(config: Configuration): Unit = if (Build.VERSION.SDK_INT >= 24) {
|
||||
val localeList = config.getLocales
|
||||
val newList = new Array[Locale](localeList.size())
|
||||
var changed = false
|
||||
for (i <- 0 until localeList.size()) {
|
||||
val locale = localeList.get(i)
|
||||
val newLocale = checkChineseLocale(locale)
|
||||
if (newLocale == null) newList(i) = locale else {
|
||||
newList(i) = newLocale
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
val newConfig = new Configuration(config)
|
||||
newConfig.setLocales(new LocaleList(newList.distinct: _*))
|
||||
val res = getResources
|
||||
res.updateConfiguration(newConfig, res.getDisplayMetrics)
|
||||
}
|
||||
} else {
|
||||
val newLocale = checkChineseLocale(config.locale)
|
||||
if (newLocale != null) {
|
||||
val newConfig = new Configuration(config)
|
||||
newConfig.locale = newLocale
|
||||
val res = getResources
|
||||
res.updateConfiguration(newConfig, res.getDisplayMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
override def onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
checkChineseLocale(newConfig)
|
||||
}
|
||||
|
||||
override def onCreate() {
|
||||
if (!BuildConfig.DEBUG) java.lang.System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
|
||||
app = this
|
||||
if (!BuildConfig.DEBUG) java.lang.System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
checkChineseLocale(getResources.getConfiguration)
|
||||
val tm = TagManager.getInstance(this)
|
||||
val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container)
|
||||
val callback = new ResultCallback[ContainerHolder] {
|
||||
@@ -111,6 +185,7 @@ class ShadowsocksApplication extends Application {
|
||||
}
|
||||
}
|
||||
pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
|
||||
JobManager.create(this).addJobCreator(DonaldTrump)
|
||||
|
||||
TcpFastOpen.enabled(settings.getBoolean(Key.tfo, TcpFastOpen.sendEnabled))
|
||||
}
|
||||
@@ -119,4 +194,45 @@ class ShadowsocksApplication extends Application {
|
||||
val holder = app.containerHolder
|
||||
if (holder != null) holder.refresh()
|
||||
}
|
||||
|
||||
private def copyAssets(path: String) {
|
||||
val assetManager = getAssets
|
||||
var files: Array[String] = null
|
||||
try files = assetManager.list(path) catch {
|
||||
case e: IOException =>
|
||||
Log.e(TAG, e.getMessage)
|
||||
app.track(e)
|
||||
}
|
||||
if (files != null) for (file <- files)
|
||||
autoClose(assetManager.open(if (path.nonEmpty) path + '/' + file else file))(in =>
|
||||
autoClose(new FileOutputStream(getApplicationInfo.dataDir + '/' + file))(out =>
|
||||
IOUtils.copy(in, out)))
|
||||
}
|
||||
|
||||
def crashRecovery() {
|
||||
val cmd = new ArrayBuffer[String]()
|
||||
|
||||
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks", "kcptun")) {
|
||||
cmd.append("killall %s".formatLocal(Locale.ENGLISH, task))
|
||||
cmd.append("rm -f %1$s/%2$s-nat.conf %1$s/%2$s-vpn.conf"
|
||||
.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
|
||||
}
|
||||
if (app.isNatEnabled) {
|
||||
cmd.append("iptables -t nat -F OUTPUT")
|
||||
cmd.append("echo done")
|
||||
val result = Shell.SU.run(cmd.toArray)
|
||||
if (result != null && !result.isEmpty) return // fallback to SH
|
||||
}
|
||||
Shell.SH.run(cmd.toArray)
|
||||
}
|
||||
|
||||
def copyAssets() {
|
||||
crashRecovery() // ensure executables are killed before writing to them
|
||||
copyAssets(System.getABI)
|
||||
copyAssets("acl")
|
||||
Shell.SH.run(EXECUTABLES.map("chmod 755 " + getApplicationInfo.dataDir + '/' + _))
|
||||
editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
|
||||
}
|
||||
|
||||
def updateAssets() = if (settings.getInt(Key.currentVersionCode, -1) != BuildConfig.VERSION_CODE) copyAssets()
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import android.os._
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.job.AclSyncJob
|
||||
import com.github.shadowsocks.utils._
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
|
||||
@@ -96,13 +97,7 @@ class ShadowsocksNatService extends BaseService {
|
||||
|
||||
if (profile.route != Route.ALL) {
|
||||
cmd += "--acl"
|
||||
profile.route match {
|
||||
case Route.BYPASS_LAN => cmd += (getApplicationInfo.dataDir + "/bypass_lan.acl")
|
||||
case Route.BYPASS_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_chn.acl")
|
||||
case Route.BYPASS_LAN_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_lan_chn.acl")
|
||||
case Route.GFWLIST => cmd += (getApplicationInfo.dataDir + "/gfwlist.acl")
|
||||
case Route.CHINALIST => cmd += (getApplicationInfo.dataDir + "/chinalist.acl")
|
||||
}
|
||||
cmd += getApplicationInfo.dataDir + '/' + profile.route + ".acl"
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
@@ -112,8 +107,14 @@ class ShadowsocksNatService extends BaseService {
|
||||
def startKcptunDaemon() {
|
||||
if (profile.kcpcli == null) profile.kcpcli = ""
|
||||
|
||||
val host = if (profile.host.contains(":")) {
|
||||
"[" + profile.host + "]"
|
||||
} else {
|
||||
profile.host
|
||||
}
|
||||
|
||||
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/kcptun"
|
||||
, "-r", profile.host + ":" + profile.kcpPort
|
||||
, "-r", host + ":" + profile.kcpPort
|
||||
, "-l", "127.0.0.1:" + (profile.localPort + 90))
|
||||
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
|
||||
case exc: Exception => throw KcpcliParseException(exc)
|
||||
@@ -188,12 +189,12 @@ class ShadowsocksNatService extends BaseService {
|
||||
val conf = profile.route match {
|
||||
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN | Route.GFWLIST => {
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getApplicationInfo.dataDir,
|
||||
"127.0.0.1", profile.localPort + 53, "1.2.4.8, 114.114.114.114",
|
||||
"127.0.0.1", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
|
||||
getBlackList, reject, profile.localPort + 63, reject)
|
||||
}
|
||||
case Route.CHINALIST => {
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getApplicationInfo.dataDir,
|
||||
"127.0.0.1", profile.localPort + 53, "8.8.8.8, 208.67.222.222",
|
||||
"127.0.0.1", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
|
||||
"", reject, profile.localPort + 63, reject)
|
||||
}
|
||||
case _ => {
|
||||
@@ -330,8 +331,9 @@ class ShadowsocksNatService extends BaseService {
|
||||
}
|
||||
|
||||
handleConnection()
|
||||
// Set DNS
|
||||
su.addCommand(Utils.FLUSH_DNS)
|
||||
|
||||
AclSyncJob.schedule(profile.route)
|
||||
|
||||
changeState(State.CONNECTED)
|
||||
notification = new ShadowsocksNotification(this, profile.name, true)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
|
||||
.setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[Shadowsocks])
|
||||
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0))
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
|
||||
if (!ShadowsocksTileService.running) builder.addAction(R.drawable.ic_navigation_close,
|
||||
builder.addAction(R.drawable.ic_navigation_close,
|
||||
service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0))
|
||||
app.profileManager.getAllProfiles match {
|
||||
case Some(profiles) => if (profiles.length > 1)
|
||||
|
||||
@@ -58,7 +58,7 @@ class ShadowsocksRunnerService extends Service with ServiceBoundContext {
|
||||
if (bgService != null) {
|
||||
if (app.isNatEnabled) startBackgroundService()
|
||||
else if (VpnService.prepare(ShadowsocksRunnerService.this) == null) startBackgroundService()
|
||||
handler.postDelayed(() => stopSelf(), 10000)
|
||||
handler.postDelayed(() => stopSelf(), 3000)
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
@@ -170,12 +170,6 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
|
||||
true
|
||||
})
|
||||
|
||||
findPreference("flush_dnscache").setOnPreferenceClickListener(_ => {
|
||||
app.track(TAG, "flush_dnscache")
|
||||
activity.flushDnsCache()
|
||||
true
|
||||
})
|
||||
|
||||
findPreference("about").setOnPreferenceClickListener((preference: Preference) => {
|
||||
app.track(TAG, "about")
|
||||
val web = new WebView(activity)
|
||||
|
||||
@@ -9,13 +9,8 @@ import com.github.shadowsocks.utils.{State, Utils}
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
object ShadowsocksTileService {
|
||||
var running: Boolean = _
|
||||
}
|
||||
|
||||
@TargetApi(24)
|
||||
final class ShadowsocksTileService extends TileService with ServiceBoundContext {
|
||||
import ShadowsocksTileService._
|
||||
|
||||
private lazy val iconIdle = Icon.createWithResource(this, R.drawable.ic_start_idle).setTint(0x80ffffff)
|
||||
private lazy val iconBusy = Icon.createWithResource(this, R.drawable.ic_start_busy)
|
||||
@@ -46,15 +41,6 @@ final class ShadowsocksTileService extends TileService with ServiceBoundContext
|
||||
|
||||
override def onServiceConnected() = callback.stateChanged(bgService.getState, bgService.getProfileName, null)
|
||||
|
||||
override def onCreate {
|
||||
super.onCreate
|
||||
running = true
|
||||
}
|
||||
override def onDestroy {
|
||||
super.onDestroy
|
||||
running = false
|
||||
}
|
||||
|
||||
override def onStartListening {
|
||||
super.onStartListening
|
||||
attachService(callback)
|
||||
|
||||
@@ -50,6 +50,7 @@ import android.os._
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.job.AclSyncJob
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
@@ -162,6 +163,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
handleConnection()
|
||||
changeState(State.CONNECTED)
|
||||
|
||||
AclSyncJob.schedule(profile.route)
|
||||
|
||||
notification = new ShadowsocksNotification(this, profile.name)
|
||||
}
|
||||
|
||||
@@ -190,8 +194,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
def startKcptunDaemon() {
|
||||
if (profile.kcpcli == null) profile.kcpcli = ""
|
||||
|
||||
val host = if (profile.host.contains(":")) {
|
||||
"[" + profile.host + "]"
|
||||
} else {
|
||||
profile.host
|
||||
}
|
||||
|
||||
val cmd = ArrayBuffer(getApplicationInfo.dataDir + "/kcptun"
|
||||
, "-r", profile.host + ":" + profile.kcpPort
|
||||
, "-r", host + ":" + profile.kcpPort
|
||||
, "-l", "127.0.0.1:" + (profile.localPort + 90)
|
||||
, "--path", protectPath)
|
||||
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
|
||||
@@ -251,13 +261,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
if (profile.route != Route.ALL) {
|
||||
cmd += "--acl"
|
||||
profile.route match {
|
||||
case Route.BYPASS_LAN => cmd += (getApplicationInfo.dataDir + "/bypass_lan.acl")
|
||||
case Route.BYPASS_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_chn.acl")
|
||||
case Route.BYPASS_LAN_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_lan_chn.acl")
|
||||
case Route.GFWLIST => cmd += (getApplicationInfo.dataDir + "/gfwlist.acl")
|
||||
case Route.CHINALIST => cmd += (getApplicationInfo.dataDir + "/chinalist.acl")
|
||||
}
|
||||
cmd += getApplicationInfo.dataDir + '/' + profile.route + ".acl"
|
||||
}
|
||||
|
||||
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
|
||||
@@ -306,7 +310,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
val conf = profile.route match {
|
||||
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN | Route.GFWLIST => {
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, protect, getApplicationInfo.dataDir,
|
||||
"0.0.0.0", profile.localPort + 53, "114.114.114.114, 119.29.29.29, 1.2.4.8",
|
||||
"0.0.0.0", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
|
||||
getBlackList, reject, profile.localPort + 63, reject)
|
||||
}
|
||||
case Route.CHINALIST => {
|
||||
@@ -375,16 +379,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
builder.addRoute("8.8.0.0", 16)
|
||||
|
||||
try {
|
||||
conn = builder.establish()
|
||||
if (conn == null) changeState(State.STOPPED, getString(R.string.reboot_required))
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
ex.printStackTrace()
|
||||
app.track(ex)
|
||||
stopRunner(true, ex.getMessage)
|
||||
return -1
|
||||
}
|
||||
conn = builder.establish()
|
||||
if (conn == null) throw new NullConnectionException
|
||||
|
||||
val fd = conn.getFd
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.github.shadowsocks.job
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.evernote.android.job.Job.{Params, Result}
|
||||
import com.evernote.android.job.{Job, JobRequest}
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import com.github.shadowsocks.utils.IOUtils
|
||||
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
object AclSyncJob {
|
||||
final val TAG = "AclSyncJob"
|
||||
|
||||
def schedule(route: String) = new JobRequest.Builder(AclSyncJob.TAG + ':' + route)
|
||||
.setExecutionWindow(1, TimeUnit.DAYS.toMillis(28))
|
||||
.setRequirementsEnforced(true)
|
||||
.setRequiredNetworkType(JobRequest.NetworkType.UNMETERED)
|
||||
.setRequiresCharging(true)
|
||||
.setUpdateCurrent(true)
|
||||
.build().schedule()
|
||||
}
|
||||
|
||||
class AclSyncJob(route: String) extends Job {
|
||||
override def onRunJob(params: Params): Result = {
|
||||
val filename = route + ".acl"
|
||||
try {
|
||||
//noinspection JavaAccessorMethodCalledAsEmptyParen
|
||||
IOUtils.writeString(app.getApplicationInfo.dataDir + '/' + filename, autoClose(
|
||||
new URL("https://raw.githubusercontent.com/shadowsocks/shadowsocks-android/master/src/main/assets/acl/" +
|
||||
filename).openConnection().getInputStream())(IOUtils.readString))
|
||||
Result.SUCCESS
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
e.printStackTrace()
|
||||
Result.RESCHEDULE
|
||||
case e: Exception => // unknown failures, probably shouldn't retry
|
||||
e.printStackTrace()
|
||||
Result.FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.github.shadowsocks.job
|
||||
|
||||
import android.util.Log
|
||||
import com.evernote.android.job.JobCreator
|
||||
|
||||
/**
|
||||
* “I create jobs all day long.
|
||||
* - Donald Trump, 2015
|
||||
*
|
||||
* Source: http://www.cnn.com/2015/09/24/politics/donald-trump-marco-rubio-foreign-policy/
|
||||
*
|
||||
* @author !Mygod
|
||||
*/
|
||||
object DonaldTrump extends JobCreator {
|
||||
def create(tag: String) = {
|
||||
val parts = tag.split(":")
|
||||
parts(0) match {
|
||||
case AclSyncJob.TAG => new AclSyncJob(parts(1))
|
||||
case _ =>
|
||||
Log.w("DonaldTrump", "Unknown job tag: " + tag)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import java.io.{FileWriter, InputStream, OutputStream}
|
||||
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
object IOUtils {
|
||||
private final val BUFFER_SIZE = 32 * 1024
|
||||
|
||||
def copy(in: InputStream, out: OutputStream) {
|
||||
val buffer = new Array[Byte](BUFFER_SIZE)
|
||||
while (true) {
|
||||
val count = in.read(buffer)
|
||||
if (count >= 0) out.write(buffer, 0, count) else return
|
||||
}
|
||||
}
|
||||
|
||||
def readString(in: InputStream): String = {
|
||||
val builder = new StringBuilder()
|
||||
val buffer = new Array[Byte](BUFFER_SIZE)
|
||||
while (true) {
|
||||
val count = in.read(buffer)
|
||||
if (count >= 0) builder.append(new String(buffer, 0, count)) else return builder.toString()
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
def writeString(file: String, content: String) = autoClose(new FileWriter(file))(writer => writer.write(content))
|
||||
}
|
||||
@@ -54,7 +54,6 @@ import android.view.{Gravity, View, Window}
|
||||
import android.widget.Toast
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.{BuildConfig, ShadowsocksRunnerService}
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
import org.xbill.DNS._
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
@@ -169,19 +168,6 @@ object Utils {
|
||||
result
|
||||
}
|
||||
|
||||
// Because /sys/class/net/* isn't accessible since API level 24
|
||||
final val FLUSH_DNS = "for if in /sys/class/net/*; do " +
|
||||
"if [ \"down\" != $(cat $if/operstate) ]; then " + // up or unknown
|
||||
"ndc resolver flushif ${if##*/}; " +
|
||||
"fi " +
|
||||
"done; echo done"
|
||||
|
||||
// Blocked > 3 seconds
|
||||
def toggleAirplaneMode(context: Context) = {
|
||||
val result = Shell.SU.run(FLUSH_DNS)
|
||||
result != null && !result.isEmpty
|
||||
}
|
||||
|
||||
def resolve(host: String, addrType: Int): Option[String] = {
|
||||
try {
|
||||
val lookup = new Lookup(host, addrType)
|
||||
@@ -258,9 +244,6 @@ object Utils {
|
||||
}
|
||||
|
||||
def startSsService(context: Context) {
|
||||
val isInstalled = app.settings.getInt(Key.currentVersionCode, -1) == BuildConfig.VERSION_CODE
|
||||
if (!isInstalled) return
|
||||
|
||||
val intent = new Intent(context, classOf[ShadowsocksRunnerService])
|
||||
context.startService(intent)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user