Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4e1a42146 | ||
|
|
76108ec450 | ||
|
|
5b9a0d0659 | ||
|
|
6558f275fe | ||
|
|
c13cf6f5d2 | ||
|
|
cacde5cff1 | ||
|
|
bb2bd82d73 | ||
|
|
0a46eedba1 | ||
|
|
63bc60e513 | ||
|
|
d9eb0e5163 | ||
|
|
fc3d26d626 | ||
|
|
1bb3a6bf5d | ||
|
|
f6cab9b99c | ||
|
|
a24d29196f | ||
|
|
2eb12ee2ae | ||
|
|
ef7701982d | ||
|
|
bd942c688f | ||
|
|
3f08a66aab | ||
|
|
ae0801b71b | ||
|
|
241b84976f | ||
|
|
b76d089fb2 | ||
|
|
2f4cdbce25 |
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
|
A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
|
||||||
|
|
||||||
Help to translate shadowsocks: http://crowdin.net/project/shadowsocks/invite
|
<a href="https://play.google.com/store/apps/details?id=com.github.shadowsocks"><img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" height="48"></a>
|
||||||
|
|
||||||
[](https://play.google.com/store/apps/details?id=com.github.shadowsocks)
|
|
||||||
|
|
||||||
### CI STATUS
|
### CI STATUS
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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.5.16")
|
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.18")
|
||||||
|
|
||||||
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
|
||||||
|
|||||||
@@ -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="129"
|
android:versionCode="133"
|
||||||
android:versionName="2.9.7">
|
android:versionName="2.9.11">
|
||||||
|
|
||||||
<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"/>
|
||||||
|
|||||||
@@ -96,6 +96,7 @@
|
|||||||
<string-array name="route_entry">
|
<string-array name="route_entry">
|
||||||
<item>全局</item>
|
<item>全局</item>
|
||||||
<item>绕过局域网地址</item>
|
<item>绕过局域网地址</item>
|
||||||
|
<item>绕过中国大陆地址</item>
|
||||||
<item>绕过局域网及中国大陆地址</item>
|
<item>绕过局域网及中国大陆地址</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,15 @@ import android.content.{ComponentName, Context, Intent, ServiceConnection}
|
|||||||
import android.os.{RemoteException, IBinder}
|
import android.os.{RemoteException, IBinder}
|
||||||
import com.github.shadowsocks.aidl.{IShadowsocksServiceCallback, IShadowsocksService}
|
import com.github.shadowsocks.aidl.{IShadowsocksServiceCallback, IShadowsocksService}
|
||||||
import com.github.shadowsocks.utils.Action
|
import com.github.shadowsocks.utils.Action
|
||||||
import com.github.shadowsocks.utils.Utils
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mygod
|
* @author Mygod
|
||||||
*/
|
*/
|
||||||
trait ServiceBoundContext extends Context {
|
trait ServiceBoundContext extends Context { outer =>
|
||||||
|
|
||||||
class ShadowsocksServiceConnection extends ServiceConnection {
|
class ShadowsocksServiceConnection extends 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)
|
||||||
|
bgService.asBinder().linkToDeath(new ShadowsocksDeathRecipient(outer), 0)
|
||||||
registerCallback
|
registerCallback
|
||||||
ServiceBoundContext.this.onServiceConnected()
|
ServiceBoundContext.this.onServiceConnected()
|
||||||
}
|
}
|
||||||
@@ -33,11 +32,12 @@ trait ServiceBoundContext extends Context {
|
|||||||
} catch {
|
} catch {
|
||||||
case ignored: RemoteException => // Nothing
|
case ignored: RemoteException => // Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
def unregisterCallback = if (bgService != null && callback != null && callbackRegistered) try {
|
def unregisterCallback = if (bgService != null && callback != null && callbackRegistered) try {
|
||||||
bgService.unregisterCallback(callback)
|
bgService.unregisterCallback(callback)
|
||||||
callbackRegistered = false
|
callbackRegistered = false
|
||||||
} catch {
|
} catch {
|
||||||
case ignored: RemoteException => // Nothing
|
case ignored: RemoteException => callbackRegistered = false
|
||||||
}
|
}
|
||||||
|
|
||||||
def onServiceConnected() = ()
|
def onServiceConnected() = ()
|
||||||
|
|||||||
@@ -377,7 +377,11 @@ class Shadowsocks
|
|||||||
})
|
})
|
||||||
updateTraffic(0, 0, 0, 0)
|
updateTraffic(0, 0, 0, 0)
|
||||||
|
|
||||||
// Bind to the service
|
bindToService()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind to the service
|
||||||
|
def bindToService() {
|
||||||
handler.post(() => attachService)
|
handler.post(() => attachService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.github.shadowsocks
|
||||||
|
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.util.Log
|
||||||
|
import com.github.shadowsocks.utils.ProcessUtils._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chentaov5@gmail.com
|
||||||
|
*/
|
||||||
|
class ShadowsocksDeathRecipient(val mContext: ServiceBoundContext)
|
||||||
|
extends IBinder.DeathRecipient {
|
||||||
|
|
||||||
|
val TAG = "ShadowsocksDeathRecipient"
|
||||||
|
|
||||||
|
override def binderDied(): Unit = {
|
||||||
|
Log.d(TAG, "[ShadowsocksDeathRecipient] binder died.")
|
||||||
|
|
||||||
|
mContext match {
|
||||||
|
case ss: Shadowsocks =>
|
||||||
|
inShadowsocks(mContext) {
|
||||||
|
ss.unregisterCallback
|
||||||
|
ss.bindToService()
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -260,7 +260,7 @@ class ShadowsocksNatService extends BaseService {
|
|||||||
|
|
||||||
def startDnsDaemon() {
|
def startDnsDaemon() {
|
||||||
|
|
||||||
val conf = if (config.route == Route.BYPASS_CHN) {
|
val conf = if (config.route == Route.BYPASS_CHN || config.route == Route.BYPASS_LAN_CHN) {
|
||||||
val reject = ConfigUtils.getRejectList(getContext)
|
val reject = ConfigUtils.getRejectList(getContext)
|
||||||
val blackList = ConfigUtils.getBlackList(getContext)
|
val blackList = ConfigUtils.getBlackList(getContext)
|
||||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
def startDnsDaemon() {
|
def startDnsDaemon() {
|
||||||
val ipv6 = if (config.isIpv6) "" else "reject = ::/0;"
|
val ipv6 = if (config.isIpv6) "" else "reject = ::/0;"
|
||||||
val conf = {
|
val conf = {
|
||||||
if (config.route == Route.BYPASS_CHN) {
|
if (config.route == Route.BYPASS_CHN || config.route == Route.BYPASS_LAN_CHN) {
|
||||||
val reject = ConfigUtils.getRejectList(getContext)
|
val reject = ConfigUtils.getRejectList(getContext)
|
||||||
val blackList = ConfigUtils.getBlackList(getContext)
|
val blackList = ConfigUtils.getBlackList(getContext)
|
||||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
||||||
@@ -401,7 +401,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.route == Route.ALL) {
|
if (config.route == Route.ALL || config.route == Route.BYPASS_CHN) {
|
||||||
builder.addRoute("0.0.0.0", 0)
|
builder.addRoute("0.0.0.0", 0)
|
||||||
} else {
|
} else {
|
||||||
val privateList = getResources.getStringArray(R.array.bypass_private_route)
|
val privateList = getResources.getStringArray(R.array.bypass_private_route)
|
||||||
|
|||||||
@@ -2,12 +2,17 @@ package com.github
|
|||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import scala.util.{Success, Failure, Try}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mygod
|
* @author Mygod
|
||||||
*/
|
*/
|
||||||
package object shadowsocks {
|
package object shadowsocks {
|
||||||
def ThrowableFuture[T](f: => T) = Future(f) onFailure {
|
|
||||||
case e: Throwable => e.printStackTrace()
|
val handleFailure: PartialFunction[Try[_], Unit] = {
|
||||||
|
case Success(_) =>
|
||||||
|
case Failure(e) => e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
def ThrowableFuture[T](f: => T) = Future(f) onComplete handleFailure
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.github.shadowsocks.utils
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chentaov5@gmail.com
|
||||||
|
*/
|
||||||
|
object IOUtils {
|
||||||
|
|
||||||
|
val TAG = "IOUtils"
|
||||||
|
|
||||||
|
def autoClose[R <: {def close() : Unit}, T](in: R)(fun: => T): T = {
|
||||||
|
try {
|
||||||
|
fun
|
||||||
|
} finally if (in ne null)
|
||||||
|
try {
|
||||||
|
in.close()
|
||||||
|
} catch {
|
||||||
|
case e: Exception => Log.d(TAG, e.getMessage, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def inSafe[T](fun: => T): Option[T] = {
|
||||||
|
var res: Option[T] = None
|
||||||
|
try {
|
||||||
|
res = Some(fun)
|
||||||
|
} catch {
|
||||||
|
case e: Exception => Log.d(TAG, e.getMessage, e)
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.github.shadowsocks.utils
|
||||||
|
|
||||||
|
import java.io.{BufferedReader, FileInputStream, InputStreamReader}
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.shadowsocks.utils.IOUtils._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chentaov5@gmail.com
|
||||||
|
*/
|
||||||
|
object ProcessUtils {
|
||||||
|
|
||||||
|
def getCurrentProcessName: String = {
|
||||||
|
inSafe {
|
||||||
|
val inputStream = new FileInputStream("/proc/self/cmdline")
|
||||||
|
val reader = new BufferedReader(new InputStreamReader(inputStream))
|
||||||
|
autoClose(reader) {
|
||||||
|
val sb = new StringBuilder
|
||||||
|
var c: Int = 0
|
||||||
|
while({c = reader.read(); c > 0})
|
||||||
|
sb.append(c.asInstanceOf[Char])
|
||||||
|
sb.toString()
|
||||||
|
}
|
||||||
|
} match {
|
||||||
|
case Some(name: String) => name
|
||||||
|
case None => ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def inShadowsocks[A](context: Context)(fun: => A): Unit = {
|
||||||
|
if (context != null && getCurrentProcessName.equals(context.getPackageName)) fun
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def inVpn[A](context: Context)(fun: => A): Unit = {
|
||||||
|
if (context != null && getCurrentProcessName.equals(context.getPackageName + ":vpn")) fun
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def inNat[A](context: Context)(fun: => A): Unit = {
|
||||||
|
if (context != null && getCurrentProcessName.equals(context.getPackageName + ":nat")) fun
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user