clean up
This commit is contained in:
@@ -41,7 +41,7 @@ package com.github.shadowsocks
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.{ApplicationInfo, PackageManager}
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.{Bitmap, PixelFormat}
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
@@ -63,7 +63,6 @@ import android.widget.ListAdapter
|
||||
import android.widget.ListView
|
||||
import android.widget.TextView
|
||||
import com.actionbarsherlock.app.SherlockActivity
|
||||
import java.util.StringTokenizer
|
||||
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
|
||||
import java.io.{ByteArrayOutputStream, ByteArrayInputStream, InputStream}
|
||||
import com.nostra13.universalimageloader.core.{DisplayImageOptions, ImageLoader, ImageLoaderConfiguration}
|
||||
@@ -71,16 +70,18 @@ import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
|
||||
|
||||
case class ProxiedApp(uid: Int, name: String, var proxied: Boolean)
|
||||
|
||||
class ObjectArrayTools[T <: AnyRef](a: Array[T]) {
|
||||
def binarySearch(key: T) = {
|
||||
java.util.Arrays.binarySearch(a.asInstanceOf[Array[AnyRef]], key)
|
||||
}
|
||||
}
|
||||
|
||||
case class ListEntry(box: CheckBox, text: TextView, icon: ImageView)
|
||||
|
||||
object AppManager {
|
||||
|
||||
val PREFS_KEY_PROXYED = "Proxyed"
|
||||
|
||||
class ObjectArrayTools[T <: AnyRef](a: Array[T]) {
|
||||
def binarySearch(key: T) = {
|
||||
java.util.Arrays.binarySearch(a.asInstanceOf[Array[AnyRef]], key)
|
||||
}
|
||||
}
|
||||
|
||||
implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]) = new ObjectArrayTools(a)
|
||||
|
||||
def getProxiedApps(context: Context): Array[ProxiedApp] = {
|
||||
@@ -103,12 +104,23 @@ object AppManager {
|
||||
}
|
||||
}.toArray
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AppManager extends SherlockActivity with OnCheckedChangeListener with OnClickListener {
|
||||
|
||||
implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]) = new AppManager.ObjectArrayTools(a)
|
||||
val MSG_LOAD_START = 1
|
||||
val MSG_LOAD_FINISH = 2
|
||||
val SCHEME = "app://"
|
||||
val STUB = android.R.drawable.sym_def_app_icon
|
||||
|
||||
implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]) = new ObjectArrayTools(a)
|
||||
|
||||
var apps: Array[ProxiedApp] = null
|
||||
var appListView: ListView = null
|
||||
var overlay: TextView = null
|
||||
var progressDialog: ProgressDialog = null
|
||||
var adapter: ListAdapter = null
|
||||
var appsLoaded: Boolean = false
|
||||
|
||||
def loadApps(context: Context): Array[ProxiedApp] = {
|
||||
val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
@@ -120,23 +132,29 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
|
||||
val packageManager: PackageManager = context.getPackageManager
|
||||
val appList = packageManager.getInstalledApplications(0)
|
||||
|
||||
appList.filter (a => a.uid >= 10000
|
||||
appList.filter(a => a.uid >= 10000
|
||||
&& packageManager.getApplicationLabel(a) != null
|
||||
&& packageManager.getApplicationIcon(a) != null).map { a =>
|
||||
&& packageManager.getApplicationIcon(a) != null).map {
|
||||
a =>
|
||||
val uid = a.uid
|
||||
val userName = uid.toString
|
||||
val name = packageManager.getApplicationLabel(a).toString
|
||||
val proxied = (proxiedApps binarySearch userName) >= 0
|
||||
new ProxiedApp(uid, name, proxied)
|
||||
}.toArray
|
||||
}.toArray
|
||||
}
|
||||
|
||||
def loadApps() {
|
||||
apps = loadApps(this).sortWith((a, b) => {
|
||||
if (a == null || b == null || a.name == null || b.name == null) true
|
||||
else if (a.proxied == b.proxied) a.name < b.name
|
||||
else if (a.proxied) true
|
||||
else false
|
||||
if (a == null || b == null || a.name == null || b.name == null) {
|
||||
true
|
||||
} else if (a.proxied == b.proxied) {
|
||||
a.name < b.name
|
||||
} else if (a.proxied) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
adapter = new ArrayAdapter[ProxiedApp](this, R.layout.layout_apps_item, R.id.itemtext, apps) {
|
||||
override def getView(position: Int, view: View, parent: ViewGroup): View = {
|
||||
@@ -152,14 +170,14 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
|
||||
entry.text.setOnClickListener(AppManager.this)
|
||||
convertView.setTag(entry)
|
||||
entry.box.setOnCheckedChangeListener(AppManager.this)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
entry = convertView.getTag.asInstanceOf[ListEntry]
|
||||
}
|
||||
|
||||
val app: ProxiedApp = apps(position)
|
||||
val options =
|
||||
new DisplayImageOptions.Builder().showStubImage(STUB)
|
||||
new DisplayImageOptions.Builder()
|
||||
.showStubImage(STUB)
|
||||
.showImageForEmptyUri(STUB)
|
||||
.showImageOnFail(STUB)
|
||||
.resetViewBeforeLoading()
|
||||
@@ -203,13 +221,18 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
|
||||
super.onCreate(savedInstanceState)
|
||||
this.setContentView(R.layout.layout_apps)
|
||||
this.overlay = View.inflate(this, R.layout.overlay, null).asInstanceOf[TextView]
|
||||
getWindowManager.addView(overlay, new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT))
|
||||
getWindowManager
|
||||
.addView(overlay, new
|
||||
WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT))
|
||||
|
||||
val config =
|
||||
new ImageLoaderConfiguration.Builder(this).imageDownloader(
|
||||
new AppIconDownloader(this)).build()
|
||||
new ImageLoaderConfiguration.Builder(this)
|
||||
.imageDownloader(new AppIconDownloader(this))
|
||||
.build()
|
||||
ImageLoader.getInstance().init(config)
|
||||
|
||||
}
|
||||
|
||||
protected override def onResume() {
|
||||
@@ -236,8 +259,7 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
|
||||
if (app.proxied) {
|
||||
proxiedApps ++= app.uid.toString
|
||||
proxiedApps += '|'
|
||||
}
|
||||
)
|
||||
})
|
||||
val edit: SharedPreferences.Editor = prefs.edit
|
||||
edit.putString(AppManager.PREFS_KEY_PROXYED, proxiedApps.toString())
|
||||
edit.commit
|
||||
@@ -247,17 +269,18 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
|
||||
override def handleMessage(msg: Message) {
|
||||
msg.what match {
|
||||
case MSG_LOAD_START =>
|
||||
progressDialog = ProgressDialog.show(AppManager.this, "", getString(R.string.loading), true, true)
|
||||
progressDialog = ProgressDialog
|
||||
.show(AppManager.this, "", getString(R.string.loading), true, true)
|
||||
case MSG_LOAD_FINISH =>
|
||||
appListView.setAdapter(adapter)
|
||||
appListView.setOnScrollListener(new AbsListView.OnScrollListener {
|
||||
def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
|
||||
def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int,
|
||||
totalItemCount: Int) {
|
||||
if (visible) {
|
||||
val name: String = apps(firstVisibleItem).name
|
||||
if (name != null && name.length > 1) {
|
||||
overlay.setText(apps(firstVisibleItem).name.substring(0, 1))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
overlay.setText("*")
|
||||
}
|
||||
overlay.setVisibility(View.VISIBLE)
|
||||
@@ -285,7 +308,9 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
|
||||
class AppIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int)
|
||||
extends BaseImageDownloader(context, connectTimeout, readTimeout) {
|
||||
|
||||
def this(context: Context) { this(context, 0, 0) }
|
||||
def this(context: Context) {
|
||||
this(context, 0, 0)
|
||||
}
|
||||
|
||||
override def getStreamFromOtherSource(imageUri: String, extra: AnyRef): InputStream = {
|
||||
val uid = imageUri.substring(SCHEME.length).toInt
|
||||
@@ -298,17 +323,4 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
|
||||
}
|
||||
}
|
||||
|
||||
case class ListEntry(box: CheckBox, text: TextView, icon: ImageView)
|
||||
|
||||
var apps: Array[ProxiedApp] = null
|
||||
var appListView: ListView = null
|
||||
var overlay: TextView = null
|
||||
var progressDialog: ProgressDialog = null
|
||||
var adapter: ListAdapter = null
|
||||
var appsLoaded: Boolean = false
|
||||
|
||||
val MSG_LOAD_START = 1
|
||||
val MSG_LOAD_FINISH = 2
|
||||
val SCHEME = "app://"
|
||||
val STUB = android.R.drawable.sym_def_app_icon
|
||||
}
|
||||
@@ -50,10 +50,8 @@ import java.io._
|
||||
import java.lang.ref.WeakReference
|
||||
import android.net.VpnService
|
||||
import org.apache.http.conn.util.InetAddressUtils
|
||||
import org.xbill.DNS._
|
||||
import android.os.Message
|
||||
import scala.Some
|
||||
import java.net.{UnknownHostException, InetAddress}
|
||||
|
||||
object ShadowVpnService {
|
||||
def isServiceStarted: Boolean = {
|
||||
@@ -68,7 +66,6 @@ object ShadowVpnService {
|
||||
}
|
||||
|
||||
var sRunningInstance: WeakReference[ShadowVpnService] = null
|
||||
|
||||
}
|
||||
|
||||
class ShadowVpnService extends VpnService {
|
||||
@@ -86,14 +83,50 @@ class ShadowVpnService extends VpnService {
|
||||
|
||||
var conn: ParcelFileDescriptor = null
|
||||
var udpgw: String = null
|
||||
var notificationManager: NotificationManager = null
|
||||
var receiver: BroadcastReceiver = null
|
||||
var appHost: String = null
|
||||
var remotePort: Int = 0
|
||||
var localPort: Int = 0
|
||||
var sitekey: String = null
|
||||
var settings: SharedPreferences = null
|
||||
var isGlobalProxy: Boolean = false
|
||||
var isGFWList: Boolean = false
|
||||
var isBypassApps: Boolean = false
|
||||
var isDNSProxy: Boolean = false
|
||||
var isHTTPProxy: Boolean = false
|
||||
var encMethod: String = null
|
||||
var apps: Array[ProxiedApp] = null
|
||||
|
||||
val handler: Handler = new Handler {
|
||||
override def handleMessage(msg: Message) {
|
||||
val ed: SharedPreferences.Editor = settings.edit
|
||||
msg.what match {
|
||||
case MSG_CONNECT_START =>
|
||||
ed.putBoolean("isConnecting", true)
|
||||
case MSG_CONNECT_FINISH =>
|
||||
ed.putBoolean("isConnecting", false)
|
||||
case MSG_CONNECT_SUCCESS =>
|
||||
ed.putBoolean("isRunning", true)
|
||||
case MSG_CONNECT_FAIL =>
|
||||
ed.putBoolean("isRunning", false)
|
||||
case MSG_HOST_CHANGE =>
|
||||
ed.putString("appHost", appHost)
|
||||
case MSG_STOP_SELF =>
|
||||
destroy()
|
||||
stopSelf()
|
||||
}
|
||||
ed.commit
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
def getPid(name: String): Int = {
|
||||
try {
|
||||
val reader: BufferedReader = new BufferedReader(new FileReader(BASE + name + ".pid"))
|
||||
val line = reader.readLine
|
||||
return Integer.valueOf(line)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: FileNotFoundException => {
|
||||
Log.e(TAG, "Cannot open pid file: " + name)
|
||||
}
|
||||
@@ -110,8 +143,10 @@ class ShadowVpnService extends VpnService {
|
||||
def startShadowsocksDaemon() {
|
||||
new Thread {
|
||||
override def run() {
|
||||
val cmd: String = (BASE + "shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " + BASE + "shadowsocks.pid")
|
||||
.format(appHost, remotePort, localPort, sitekey, encMethod)
|
||||
val cmd: String = (BASE +
|
||||
"shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " +
|
||||
BASE +
|
||||
"shadowsocks.pid").format(appHost, remotePort, localPort, sitekey, encMethod)
|
||||
System.exec(cmd)
|
||||
}
|
||||
}.start()
|
||||
@@ -122,8 +157,7 @@ class ShadowVpnService extends VpnService {
|
||||
try {
|
||||
val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0)
|
||||
version = pi.versionName
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
version = "Package name not found"
|
||||
}
|
||||
@@ -141,16 +175,14 @@ class ShadowVpnService extends VpnService {
|
||||
encMethod = settings.getString("encMethod", "table")
|
||||
try {
|
||||
remotePort = Integer.valueOf(settings.getString("remotePort", "1984"))
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: NumberFormatException => {
|
||||
remotePort = 1984
|
||||
}
|
||||
}
|
||||
try {
|
||||
localPort = Integer.valueOf(settings.getString("port", "1984"))
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: NumberFormatException => {
|
||||
localPort = 1984
|
||||
}
|
||||
@@ -214,8 +246,7 @@ class ShadowVpnService extends VpnService {
|
||||
t.start()
|
||||
try {
|
||||
t.join(300)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ignored: InterruptedException => {
|
||||
}
|
||||
}
|
||||
@@ -249,8 +280,7 @@ class ShadowVpnService extends VpnService {
|
||||
val prefix = Array(addr, i.toString).mkString(".")
|
||||
if (prefix != prefix3) builder.addRoute(prefix + ".0", 24)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
builder.addRoute("8.8.0.0", 16)
|
||||
} else {
|
||||
for (i <- 1 to 254) {
|
||||
@@ -274,9 +304,11 @@ class ShadowVpnService extends VpnService {
|
||||
|
||||
val fd = conn.getFd
|
||||
|
||||
val cmd = (BASE + "tun2socks --netif-ipaddr 172.16.0.2 --udpgw-remote-server-addr %s:7300 " +
|
||||
"--netif-netmask 255.255.255.0 --socks-server-addr 127.0.0.1:%d --tunfd %d --tunmtu %d --pid " + BASE + "tun2socks.pid")
|
||||
.format(udpgw, localPort, fd, VPN_MTU)
|
||||
val cmd = (BASE +
|
||||
"tun2socks --netif-ipaddr 172.16.0.2 --udpgw-remote-server-addr %s:7300 " +
|
||||
"--netif-netmask 255.255.255.0 --socks-server-addr 127.0.0.1:%d --tunfd %d --tunmtu %d --pid " +
|
||||
BASE +
|
||||
"tun2socks.pid").format(udpgw, localPort, fd, VPN_MTU)
|
||||
|
||||
Log.d(TAG, cmd)
|
||||
System.exec(cmd)
|
||||
@@ -308,9 +340,12 @@ class ShadowVpnService extends VpnService {
|
||||
val contentIntent: PendingIntent = PendingIntent.getActivity(this, 0, openIntent, 0)
|
||||
val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this)
|
||||
builder
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0)
|
||||
.setTicker(title).setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(info).setContentIntent(contentIntent)
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
|
||||
.setWhen(0)
|
||||
.setTicker(title)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(info)
|
||||
.setContentIntent(contentIntent)
|
||||
.setAutoCancel(true)
|
||||
notificationManager.notify(1, builder.build)
|
||||
}
|
||||
@@ -328,7 +363,8 @@ class ShadowVpnService extends VpnService {
|
||||
EasyTracker.getTracker.sendEvent("service", "start", getVersionName, 0L)
|
||||
settings = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE).asInstanceOf[NotificationManager]
|
||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
.asInstanceOf[NotificationManager]
|
||||
|
||||
// register close receiver
|
||||
val filter = new IntentFilter()
|
||||
@@ -337,7 +373,8 @@ class ShadowVpnService extends VpnService {
|
||||
receiver = new BroadcastReceiver {
|
||||
def onReceive(p1: Context, p2: Intent) {
|
||||
destroy()
|
||||
stopSelf()}
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
registerReceiver(receiver, filter)
|
||||
}
|
||||
@@ -396,42 +433,4 @@ class ShadowVpnService extends VpnService {
|
||||
override def onRevoke() {
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
val handler: Handler = new Handler {
|
||||
override def handleMessage(msg: Message) {
|
||||
val ed: SharedPreferences.Editor = settings.edit
|
||||
msg.what match {
|
||||
case MSG_CONNECT_START =>
|
||||
ed.putBoolean("isConnecting", true)
|
||||
case MSG_CONNECT_FINISH =>
|
||||
ed.putBoolean("isConnecting", false)
|
||||
case MSG_CONNECT_SUCCESS =>
|
||||
ed.putBoolean("isRunning", true)
|
||||
case MSG_CONNECT_FAIL =>
|
||||
ed.putBoolean("isRunning", false)
|
||||
case MSG_HOST_CHANGE =>
|
||||
ed.putString("appHost", appHost)
|
||||
case MSG_STOP_SELF =>
|
||||
destroy()
|
||||
stopSelf()
|
||||
}
|
||||
ed.commit
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
var notificationManager: NotificationManager = null
|
||||
var receiver: BroadcastReceiver = null
|
||||
var appHost: String = null
|
||||
var remotePort: Int = 0
|
||||
var localPort: Int = 0
|
||||
var sitekey: String = null
|
||||
var settings: SharedPreferences = null
|
||||
var isGlobalProxy: Boolean = false
|
||||
var isGFWList: Boolean = false
|
||||
var isBypassApps: Boolean = false
|
||||
var isDNSProxy: Boolean = false
|
||||
var isHTTPProxy: Boolean = false
|
||||
var encMethod: String = null
|
||||
var apps: Array[ProxiedApp] = null
|
||||
}
|
||||
@@ -68,22 +68,22 @@ import net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity
|
||||
import org.jraf.android.backport.switchwidget.Switch
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.{Uri, VpnService}
|
||||
import android.text.SpannableString
|
||||
import android.text.util.Linkify
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.webkit.{WebViewClient, WebView}
|
||||
|
||||
object Shadowsocks {
|
||||
|
||||
val PREFS_NAME = "Shadowsocks"
|
||||
val PROXY_PREFS = Array("proxy", "remotePort", "port", "sitekey", "encMethod")
|
||||
val FEATRUE_PREFS = Array("isHTTPProxy", "isDNSProxy", "isGFWList", "isGlobalProxy", "isBypassApps", "proxyedApps", "isAutoConnect")
|
||||
val FEATRUE_PREFS = Array("isHTTPProxy", "isDNSProxy", "isGFWList", "isGlobalProxy",
|
||||
"isBypassApps", "proxyedApps", "isAutoConnect")
|
||||
val TAG = "Shadowsocks"
|
||||
val REQUEST_CONNECT = 1
|
||||
|
||||
class ProxyFragment extends UnifiedPreferenceFragment with OnSharedPreferenceChangeListener {
|
||||
private def setPreferenceEnabled() {
|
||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity)
|
||||
val enabled: Boolean = !settings.getBoolean("isRunning", false) && !settings.getBoolean("isConnecting", false)
|
||||
val enabled: Boolean = !settings.getBoolean("isRunning", false) &&
|
||||
!settings.getBoolean("isConnecting", false)
|
||||
for (name <- PROXY_PREFS) {
|
||||
val pref: Preference = findPreference(name)
|
||||
if (pref != null) {
|
||||
@@ -113,7 +113,8 @@ object Shadowsocks {
|
||||
class FeatureFragment extends UnifiedPreferenceFragment with OnSharedPreferenceChangeListener {
|
||||
private def setPreferenceEnabled() {
|
||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity)
|
||||
val enabled: Boolean = !settings.getBoolean("isRunning", false) && !settings.getBoolean("isConnecting", false)
|
||||
val enabled: Boolean = !settings.getBoolean("isRunning", false) &&
|
||||
!settings.getBoolean("isConnecting", false)
|
||||
for (name <- Shadowsocks.FEATRUE_PREFS) {
|
||||
val pref: Preference = findPreference(name)
|
||||
if (pref != null) {
|
||||
@@ -156,8 +157,7 @@ object Typefaces {
|
||||
try {
|
||||
val t: Typeface = Typeface.createFromAsset(c.getAssets, assetPath)
|
||||
cache.put(assetPath, t)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: Exception => {
|
||||
Log.e(TAG, "Could not get typeface '" + assetPath + "' because " + e.getMessage)
|
||||
return null
|
||||
@@ -172,7 +172,9 @@ object Typefaces {
|
||||
private final val cache: Hashtable[String, Typeface] = new Hashtable[String, Typeface]
|
||||
}
|
||||
|
||||
class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.OnCheckedChangeListener with OnSharedPreferenceChangeListener {
|
||||
class Shadowsocks
|
||||
extends UnifiedSherlockPreferenceActivity with CompoundButton.OnCheckedChangeListener with
|
||||
OnSharedPreferenceChangeListener {
|
||||
|
||||
private val MSG_CRASH_RECOVER: Int = 1
|
||||
private val MSG_INITIAL_FINISH: Int = 2
|
||||
@@ -181,13 +183,32 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
private var progressDialog: ProgressDialog = null
|
||||
private var prepared = false
|
||||
|
||||
private val handler: Handler = new Handler {
|
||||
override def handleMessage(msg: Message) {
|
||||
val settings: SharedPreferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(Shadowsocks.this)
|
||||
val ed: SharedPreferences.Editor = settings.edit
|
||||
msg.what match {
|
||||
case MSG_CRASH_RECOVER =>
|
||||
Crouton.makeText(Shadowsocks.this, R.string.crash_alert, Style.ALERT).show()
|
||||
ed.putBoolean("isRunning", false)
|
||||
case MSG_INITIAL_FINISH =>
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss()
|
||||
progressDialog = null
|
||||
}
|
||||
}
|
||||
ed.commit
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
private def copyAssets(path: String) {
|
||||
val assetManager: AssetManager = getAssets
|
||||
var files: Array[String] = null
|
||||
try {
|
||||
files = assetManager.list(path)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: IOException => {
|
||||
Log.e(Shadowsocks.TAG, e.getMessage)
|
||||
}
|
||||
@@ -199,8 +220,7 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
try {
|
||||
if (path.length > 0) {
|
||||
in = assetManager.open(path + "/" + file)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
in = assetManager.open(file)
|
||||
}
|
||||
out = new FileOutputStream("/data/data/com.github.shadowsocks/" + file)
|
||||
@@ -210,8 +230,7 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
out.flush()
|
||||
out.close()
|
||||
out = null
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
Log.e(Shadowsocks.TAG, ex.getMessage)
|
||||
}
|
||||
@@ -287,7 +306,9 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
override def onCreate(savedInstanceState: Bundle) {
|
||||
setHeaderRes(R.xml.shadowsocks_headers)
|
||||
super.onCreate(savedInstanceState)
|
||||
val switchLayout: RelativeLayout = getLayoutInflater.inflate(R.layout.layout_switch, null).asInstanceOf[RelativeLayout]
|
||||
val switchLayout: RelativeLayout = getLayoutInflater
|
||||
.inflate(R.layout.layout_switch, null)
|
||||
.asInstanceOf[RelativeLayout]
|
||||
getSupportActionBar.setCustomView(switchLayout)
|
||||
getSupportActionBar.setDisplayShowTitleEnabled(false)
|
||||
getSupportActionBar.setDisplayShowCustomEnabled(true)
|
||||
@@ -297,8 +318,10 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
val tf: Typeface = Typefaces.get(this, "fonts/Iceland.ttf")
|
||||
if (tf != null) title.setTypeface(tf)
|
||||
title.setText(R.string.app_name)
|
||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(Shadowsocks.this)
|
||||
val init: Boolean = !settings.getBoolean("isRunning", false) && !settings.getBoolean("isConnecting", false)
|
||||
val settings: SharedPreferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(Shadowsocks.this)
|
||||
val init: Boolean = !settings.getBoolean("isRunning", false) &&
|
||||
!settings.getBoolean("isConnecting", false)
|
||||
if (init) {
|
||||
if (progressDialog == null) {
|
||||
progressDialog = ProgressDialog.show(this, "", getString(R.string.initializing), true, true)
|
||||
@@ -309,8 +332,7 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
var versionName: String = null
|
||||
try {
|
||||
versionName = getPackageManager.getPackageInfo(getPackageName, 0).versionName
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
versionName = "NONE"
|
||||
}
|
||||
@@ -328,9 +350,18 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
}
|
||||
|
||||
override def onCreateOptionsMenu(menu: Menu): Boolean = {
|
||||
menu.add(0, 0, 0, R.string.recovery).setIcon(android.R.drawable.ic_menu_revert).setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT)
|
||||
menu.add(0, 1, 1, R.string.about).setIcon(android.R.drawable.ic_menu_info_details).setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT)
|
||||
menu.add(0, 2, 2, R.string.donate).setIcon(android.R.drawable.ic_menu_info_details).setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT)
|
||||
menu
|
||||
.add(0, 0, 0, R.string.recovery)
|
||||
.setIcon(android.R.drawable.ic_menu_revert)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT)
|
||||
menu
|
||||
.add(0, 1, 1, R.string.about)
|
||||
.setIcon(android.R.drawable.ic_menu_info_details)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT)
|
||||
menu
|
||||
.add(0, 2, 2, R.string.donate)
|
||||
.setIcon(android.R.drawable.ic_menu_info_details)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT)
|
||||
true
|
||||
}
|
||||
|
||||
@@ -338,8 +369,7 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount == 0) {
|
||||
try {
|
||||
finish()
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ignore: Exception => {
|
||||
}
|
||||
}
|
||||
@@ -363,7 +393,9 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
protected override def onPause() {
|
||||
super.onPause()
|
||||
prepared = false
|
||||
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this)
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(this)
|
||||
.unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
protected override def onResume() {
|
||||
@@ -396,12 +428,16 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
}
|
||||
setPreferenceEnabled()
|
||||
switchButton.setOnCheckedChangeListener(this)
|
||||
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this)
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(this)
|
||||
.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
private def setPreferenceEnabled() {
|
||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(Shadowsocks.this)
|
||||
val enabled: Boolean = !settings.getBoolean("isRunning", false) && !settings.getBoolean("isConnecting", false)
|
||||
val settings: SharedPreferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(Shadowsocks.this)
|
||||
val enabled: Boolean = !settings.getBoolean("isRunning", false) &&
|
||||
!settings.getBoolean("isConnecting", false)
|
||||
for (name <- Shadowsocks.PROXY_PREFS) {
|
||||
val pref: Preference = findPreference(name)
|
||||
if (pref != null) {
|
||||
@@ -443,8 +479,7 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
if (progressDialog == null) {
|
||||
progressDialog = ProgressDialog.show(this, "", getString(R.string.connecting), true, true)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Log.d(Shadowsocks.TAG, "Connecting finish")
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss()
|
||||
@@ -554,8 +589,7 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
this.showDialog(getString(R.string.port_alert))
|
||||
return false
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
this.showDialog(getString(R.string.port_alert))
|
||||
return false
|
||||
@@ -601,18 +635,25 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
}
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(this).setTitle(getString(R.string.about_title).format(versionName))
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(getString(R.string.about_title).format(versionName))
|
||||
.setCancelable(false)
|
||||
.setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener() {
|
||||
override def onClick(dialog: DialogInterface, id: Int) {
|
||||
dialog.cancel()
|
||||
}
|
||||
}).setView(web).create().show()
|
||||
})
|
||||
.setView(web)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
private def showDialog(msg: String) {
|
||||
val builder: AlertDialog.Builder = new AlertDialog.Builder(this)
|
||||
builder.setMessage(msg).setCancelable(false).setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener {
|
||||
builder
|
||||
.setMessage(msg)
|
||||
.setCancelable(false)
|
||||
.setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener {
|
||||
def onClick(dialog: DialogInterface, id: Int) {
|
||||
dialog.cancel()
|
||||
}
|
||||
@@ -620,24 +661,4 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton.
|
||||
val alert: AlertDialog = builder.create
|
||||
alert.show()
|
||||
}
|
||||
|
||||
private[shadowsocks] final val handler: Handler = new Handler {
|
||||
override def handleMessage(msg: Message) {
|
||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(Shadowsocks.this)
|
||||
val ed: SharedPreferences.Editor = settings.edit
|
||||
msg.what match {
|
||||
case MSG_CRASH_RECOVER =>
|
||||
Crouton.makeText(Shadowsocks.this, R.string.crash_alert, Style.ALERT).show()
|
||||
ed.putBoolean("isRunning", false)
|
||||
case MSG_INITIAL_FINISH =>
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss()
|
||||
progressDialog = null
|
||||
}
|
||||
}
|
||||
ed.commit
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,7 +44,6 @@ import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.preference.PreferenceManager
|
||||
|
||||
|
||||
class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
|
||||
val TAG = "Shadowsocks"
|
||||
@@ -54,8 +53,7 @@ class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
var versionName: String = null
|
||||
try {
|
||||
versionName = context.getPackageManager.getPackageInfo(context.getPackageName, 0).versionName
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
versionName = "NONE"
|
||||
}
|
||||
@@ -72,8 +70,7 @@ class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
if (port <= 1024) {
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: Exception => {
|
||||
return
|
||||
}
|
||||
@@ -84,7 +81,6 @@ class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
val it: Intent = new Intent(context, classOf[ShadowsocksService])
|
||||
context.startService(it)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,10 +61,7 @@ import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Method
|
||||
import org.apache.http.conn.util.InetAddressUtils
|
||||
import scala.collection._
|
||||
import org.xbill.DNS._
|
||||
import scala.Some
|
||||
import scala.Some
|
||||
import java.net.{UnknownHostException, InetAddress}
|
||||
|
||||
object ShadowsocksService {
|
||||
def isServiceStarted: Boolean = {
|
||||
@@ -83,13 +80,68 @@ object ShadowsocksService {
|
||||
|
||||
class ShadowsocksService extends Service {
|
||||
|
||||
val TAG = "ShadowsocksService"
|
||||
val BASE = "/data/data/com.github.shadowsocks/"
|
||||
val REDSOCKS_CONF = "base {" +
|
||||
" log_debug = off;" +
|
||||
" log_info = off;" +
|
||||
" log = stderr;" +
|
||||
" daemon = on;" +
|
||||
" redirector = iptables;" +
|
||||
"}" +
|
||||
"redsocks {" +
|
||||
" local_ip = 127.0.0.1;" +
|
||||
" local_port = 8123;" +
|
||||
" ip = 127.0.0.1;" +
|
||||
" port = %d;" +
|
||||
" type = socks5;" +
|
||||
"}"
|
||||
val SHADOWSOCKS_CONF = "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
|
||||
val CMD_IPTABLES_RETURN = " -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
|
||||
val CMD_IPTABLES_REDIRECT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j REDIRECT --to 8123\n"
|
||||
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " +
|
||||
"-j DNAT --to-destination 127.0.0.1:8123\n"
|
||||
val MSG_CONNECT_START: Int = 0
|
||||
val MSG_CONNECT_FINISH: Int = 1
|
||||
val MSG_CONNECT_SUCCESS: Int = 2
|
||||
val MSG_CONNECT_FAIL: Int = 3
|
||||
val MSG_HOST_CHANGE: Int = 4
|
||||
val MSG_STOP_SELF: Int = 5
|
||||
val DNS_PORT: Int = 8153
|
||||
|
||||
val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
|
||||
val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
|
||||
val mSetForegroundSignature = Array[Class[_]](classOf[Boolean])
|
||||
|
||||
var receiver: BroadcastReceiver = null
|
||||
var notificationManager: NotificationManager = null
|
||||
var mWakeLock: PowerManager#WakeLock = null
|
||||
var appHost: String = null
|
||||
var remotePort: Int = 0
|
||||
var localPort: Int = 0
|
||||
var sitekey: String = null
|
||||
var settings: SharedPreferences = null
|
||||
var hasRedirectSupport: Boolean = true
|
||||
var isGlobalProxy: Boolean = false
|
||||
var isGFWList: Boolean = false
|
||||
var isBypassApps: Boolean = false
|
||||
var isDNSProxy: Boolean = false
|
||||
var isHTTPProxy: Boolean = false
|
||||
var encMethod: String = null
|
||||
var apps: Array[ProxiedApp] = null
|
||||
var mSetForeground: Method = null
|
||||
var mStartForeground: Method = null
|
||||
var mStopForeground: Method = null
|
||||
var mSetForegroundArgs = new Array[AnyRef](1)
|
||||
var mStartForegroundArgs = new Array[AnyRef](2)
|
||||
var mStopForegroundArgs = new Array[AnyRef](1)
|
||||
|
||||
def getPid(name: String): Int = {
|
||||
try {
|
||||
val reader: BufferedReader = new BufferedReader(new FileReader(BASE + name + ".pid"))
|
||||
val line = reader.readLine
|
||||
return Integer.valueOf(line)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: FileNotFoundException => {
|
||||
Log.e(TAG, "Cannot open pid file: " + name)
|
||||
}
|
||||
@@ -106,8 +158,10 @@ class ShadowsocksService extends Service {
|
||||
def startShadowsocksDaemon() {
|
||||
new Thread {
|
||||
override def run() {
|
||||
val cmd: String = (BASE + "shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " + BASE + "shadowsocks.pid")
|
||||
.format(appHost, remotePort, localPort, sitekey, encMethod)
|
||||
val cmd: String = (BASE +
|
||||
"shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " +
|
||||
BASE +
|
||||
"shadowsocks.pid").format(appHost, remotePort, localPort, sitekey, encMethod)
|
||||
System.exec(cmd)
|
||||
}
|
||||
}.start()
|
||||
@@ -116,7 +170,8 @@ class ShadowsocksService extends Service {
|
||||
def startPolipoDaemon() {
|
||||
new Thread {
|
||||
override def run() {
|
||||
val cmd: String = (BASE + "polipo proxyPort=%d socksParentProxy=127.0.0.1:%d daemonise=true pidFile=\"%s\" logLevel=1 logFile=\"%s\"")
|
||||
val cmd: String = (BASE +
|
||||
"polipo proxyPort=%d socksParentProxy=127.0.0.1:%d daemonise=true pidFile=\"%s\" logLevel=1 logFile=\"%s\"")
|
||||
.format(localPort + 1, localPort, BASE + "polipo.pid", BASE + "polipo.log")
|
||||
System.exec(cmd)
|
||||
}
|
||||
@@ -133,8 +188,7 @@ class ShadowsocksService extends Service {
|
||||
try {
|
||||
val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0)
|
||||
version = pi.versionName
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
version = "Package name not found"
|
||||
}
|
||||
@@ -152,16 +206,14 @@ class ShadowsocksService extends Service {
|
||||
encMethod = settings.getString("encMethod", "table")
|
||||
try {
|
||||
remotePort = Integer.valueOf(settings.getString("remotePort", "1984"))
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: NumberFormatException => {
|
||||
remotePort = 1984
|
||||
}
|
||||
}
|
||||
try {
|
||||
localPort = Integer.valueOf(settings.getString("port", "1984"))
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: NumberFormatException => {
|
||||
localPort = 1984
|
||||
}
|
||||
@@ -192,10 +244,10 @@ class ShadowsocksService extends Service {
|
||||
Log.d(TAG, "IPTABLES: " + Utils.getIptables)
|
||||
hasRedirectSupport = Utils.getHasRedirectSupport
|
||||
if (resolved && handleConnection) {
|
||||
notifyForegroundAlert(getString(R.string.forward_success), getString(R.string.service_running))
|
||||
notifyForegroundAlert(getString(R.string.forward_success),
|
||||
getString(R.string.service_running))
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_SUCCESS, 500)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
stopSelf()
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_FAIL, 500)
|
||||
@@ -225,8 +277,7 @@ class ShadowsocksService extends Service {
|
||||
t.start()
|
||||
try {
|
||||
t.join(300)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ignored: InterruptedException => {
|
||||
}
|
||||
}
|
||||
@@ -253,8 +304,7 @@ class ShadowsocksService extends Service {
|
||||
def invokeMethod(method: Method, args: Array[AnyRef]) {
|
||||
try {
|
||||
method.invoke(this, mStartForegroundArgs: _*)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: InvocationTargetException => {
|
||||
Log.w(TAG, "Unable to invoke method", e)
|
||||
}
|
||||
@@ -280,10 +330,14 @@ class ShadowsocksService extends Service {
|
||||
val actionIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0)
|
||||
val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this)
|
||||
builder
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0)
|
||||
.setTicker(title).setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(info).setContentIntent(contentIntent)
|
||||
.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), actionIntent)
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
|
||||
.setWhen(0)
|
||||
.setTicker(title)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(info)
|
||||
.setContentIntent(contentIntent)
|
||||
.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop),
|
||||
actionIntent)
|
||||
startForegroundCompat(1, builder.build)
|
||||
}
|
||||
|
||||
@@ -293,10 +347,12 @@ class ShadowsocksService extends Service {
|
||||
val contentIntent: PendingIntent = PendingIntent.getActivity(this, 0, openIntent, 0)
|
||||
val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this)
|
||||
builder
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0)
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
|
||||
.setWhen(0)
|
||||
.setTicker(title)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(info).setContentIntent(contentIntent)
|
||||
.setContentText(info)
|
||||
.setContentIntent(contentIntent)
|
||||
.setAutoCancel(true)
|
||||
notificationManager.notify(1, builder.build)
|
||||
}
|
||||
@@ -309,12 +365,13 @@ class ShadowsocksService extends Service {
|
||||
super.onCreate()
|
||||
EasyTracker.getTracker.sendEvent("service", "start", getVersionName, 0L)
|
||||
settings = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
notificationManager = this.getSystemService(Context.NOTIFICATION_SERVICE).asInstanceOf[NotificationManager]
|
||||
notificationManager = this
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
.asInstanceOf[NotificationManager]
|
||||
try {
|
||||
mStartForeground = getClass.getMethod("startForeground", mStartForegroundSignature: _*)
|
||||
mStopForeground = getClass.getMethod("stopForeground", mStopForegroundSignature: _*)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: NoSuchMethodException => {
|
||||
mStartForeground = ({
|
||||
mStopForeground = null
|
||||
@@ -324,10 +381,10 @@ class ShadowsocksService extends Service {
|
||||
}
|
||||
try {
|
||||
mSetForeground = getClass.getMethod("setForeground", mSetForegroundSignature: _*)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: NoSuchMethodException => {
|
||||
throw new IllegalStateException("OS doesn't have Service.startForeground OR Service.setForeground!")
|
||||
throw new IllegalStateException(
|
||||
"OS doesn't have Service.startForeground OR Service.setForeground!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,10 +393,11 @@ class ShadowsocksService extends Service {
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||
filter.addAction(Utils.CLOSE_ACTION)
|
||||
receiver = new BroadcastReceiver() {
|
||||
def onReceive(p1: Context, p2: Intent) { stopSelf() }
|
||||
def onReceive(p1: Context, p2: Intent) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
registerReceiver(receiver, filter)
|
||||
|
||||
}
|
||||
|
||||
/** Called when the activity is closed. */
|
||||
@@ -412,13 +470,21 @@ class ShadowsocksService extends Service {
|
||||
init_sb.append(cmd_bypass.replace("0.0.0.0", "127.0.0.1"))
|
||||
if (!isDNSProxy) {
|
||||
init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport " + 53))
|
||||
init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + getApplicationInfo.uid))
|
||||
init_sb
|
||||
.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + getApplicationInfo.uid))
|
||||
}
|
||||
if (hasRedirectSupport) {
|
||||
init_sb.append(Utils.getIptables).append(" -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to ").append(DNS_PORT).append("\n")
|
||||
}
|
||||
else {
|
||||
init_sb.append(Utils.getIptables).append(" -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:").append(DNS_PORT).append("\n")
|
||||
init_sb
|
||||
.append(Utils.getIptables)
|
||||
.append(" -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to ")
|
||||
.append(DNS_PORT)
|
||||
.append("\n")
|
||||
} else {
|
||||
init_sb
|
||||
.append(Utils.getIptables)
|
||||
.append(" -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:")
|
||||
.append(DNS_PORT)
|
||||
.append("\n")
|
||||
}
|
||||
if (isGFWList) {
|
||||
val chn_list: Array[String] = getResources.getStringArray(R.array.chn_list)
|
||||
@@ -427,7 +493,12 @@ class ShadowsocksService extends Service {
|
||||
}
|
||||
}
|
||||
if (isGlobalProxy || isBypassApps) {
|
||||
http_sb.append(if (hasRedirectSupport) Utils.getIptables + CMD_IPTABLES_REDIRECT_ADD_SOCKS else Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS)
|
||||
http_sb
|
||||
.append(if (hasRedirectSupport) {
|
||||
Utils.getIptables + CMD_IPTABLES_REDIRECT_ADD_SOCKS
|
||||
} else {
|
||||
Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS
|
||||
})
|
||||
}
|
||||
if (!isGlobalProxy) {
|
||||
if (apps == null || apps.length <= 0) apps = AppManager.getProxiedApps(this)
|
||||
@@ -439,9 +510,14 @@ class ShadowsocksService extends Service {
|
||||
}
|
||||
for (uid <- uidSet) {
|
||||
if (!isBypassApps) {
|
||||
http_sb.append((if (hasRedirectSupport) Utils.getIptables + CMD_IPTABLES_REDIRECT_ADD_SOCKS else Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS).replace("-t nat", "-t nat -m owner --uid-owner " + uid))
|
||||
}
|
||||
else {
|
||||
http_sb
|
||||
.append((if (hasRedirectSupport) {
|
||||
Utils.getIptables + CMD_IPTABLES_REDIRECT_ADD_SOCKS
|
||||
} else {
|
||||
Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS
|
||||
})
|
||||
.replace("-t nat", "-t nat -m owner --uid-owner " + uid))
|
||||
} else {
|
||||
init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid))
|
||||
}
|
||||
}
|
||||
@@ -478,8 +554,7 @@ class ShadowsocksService extends Service {
|
||||
mStopForegroundArgs(0) = boolean2Boolean(x = true)
|
||||
try {
|
||||
mStopForeground.invoke(this, mStopForegroundArgs: _*)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: InvocationTargetException => {
|
||||
Log.w(TAG, "Unable to invoke stopForeground", e)
|
||||
}
|
||||
@@ -501,7 +576,9 @@ class ShadowsocksService extends Service {
|
||||
case MSG_CONNECT_START =>
|
||||
ed.putBoolean("isConnecting", true)
|
||||
val pm: PowerManager = getSystemService(Context.POWER_SERVICE).asInstanceOf[PowerManager]
|
||||
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "GAEProxy")
|
||||
mWakeLock = pm
|
||||
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
|
||||
"GAEProxy")
|
||||
mWakeLock.acquire()
|
||||
case MSG_CONNECT_FINISH =>
|
||||
ed.putBoolean("isConnecting", false)
|
||||
@@ -519,46 +596,4 @@ class ShadowsocksService extends Service {
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
val TAG = "ShadowsocksService"
|
||||
val BASE = "/data/data/com.github.shadowsocks/"
|
||||
val REDSOCKS_CONF = "base {" + " log_debug = off;" + " log_info = off;" + " log = stderr;" + " daemon = on;" + " redirector = iptables;" + "}" + "redsocks {" + " local_ip = 127.0.0.1;" + " local_port = 8123;" + " ip = 127.0.0.1;" + " port = %d;" + " type = socks5;" + "}"
|
||||
val SHADOWSOCKS_CONF = "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
|
||||
val CMD_IPTABLES_RETURN = " -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
|
||||
val CMD_IPTABLES_REDIRECT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j REDIRECT --to 8123\n"
|
||||
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j DNAT --to-destination 127.0.0.1:8123\n"
|
||||
val MSG_CONNECT_START: Int = 0
|
||||
val MSG_CONNECT_FINISH: Int = 1
|
||||
val MSG_CONNECT_SUCCESS: Int = 2
|
||||
val MSG_CONNECT_FAIL: Int = 3
|
||||
val MSG_HOST_CHANGE: Int = 4
|
||||
val MSG_STOP_SELF: Int = 5
|
||||
val DNS_PORT: Int = 8153
|
||||
|
||||
val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
|
||||
val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
|
||||
val mSetForegroundSignature = Array[Class[_]](classOf[Boolean])
|
||||
|
||||
var receiver: BroadcastReceiver = null
|
||||
var notificationManager: NotificationManager = null
|
||||
var mWakeLock: PowerManager#WakeLock = null
|
||||
var appHost: String = null
|
||||
var remotePort: Int = 0
|
||||
var localPort: Int = 0
|
||||
var sitekey: String = null
|
||||
var settings: SharedPreferences = null
|
||||
var hasRedirectSupport: Boolean = true
|
||||
var isGlobalProxy: Boolean = false
|
||||
var isGFWList: Boolean = false
|
||||
var isBypassApps: Boolean = false
|
||||
var isDNSProxy: Boolean = false
|
||||
var isHTTPProxy: Boolean = false
|
||||
var encMethod: String = null
|
||||
var apps: Array[ProxiedApp] = null
|
||||
var mSetForeground: Method = null
|
||||
var mStartForeground: Method = null
|
||||
var mStopForeground: Method = null
|
||||
var mSetForegroundArgs = new Array[AnyRef](1)
|
||||
var mStartForegroundArgs = new Array[AnyRef](2)
|
||||
var mStopForegroundArgs = new Array[AnyRef](1)
|
||||
}
|
||||
@@ -53,6 +53,27 @@ import android.graphics.{Canvas, Bitmap}
|
||||
|
||||
object Utils {
|
||||
|
||||
val CLOSE_ACTION = "com.github.shadowsocks.ACTION_SHUTDOWN"
|
||||
val TAG: String = "Shadowsocks"
|
||||
val ABI_PROP: String = "ro.product.cpu.abi"
|
||||
val ABI2_PROP: String = "ro.product.cpu.abi2"
|
||||
val ARM_ABI: String = "arm"
|
||||
val X86_ABI: String = "x86"
|
||||
val MIPS_ABI: String = "mips"
|
||||
val DEFAULT_SHELL: String = "/system/bin/sh"
|
||||
val DEFAULT_ROOT: String = "/system/bin/su"
|
||||
val ALTERNATIVE_ROOT: String = "/system/xbin/su"
|
||||
val DEFAULT_IPTABLES: String = "/data/data/com.github.shadowsocks/iptables"
|
||||
val ALTERNATIVE_IPTABLES: String = "/system/bin/iptables"
|
||||
val TIME_OUT: Int = -99
|
||||
var initialized: Boolean = false
|
||||
var hasRedirectSupport: Int = -1
|
||||
var isRoot: Int = -1
|
||||
var shell: String = null
|
||||
var root_shell: String = null
|
||||
var iptables: String = null
|
||||
var data_path: String = null
|
||||
var rootTries = 0
|
||||
|
||||
def resolve(host: String, addrType: Int): Option[String] = {
|
||||
val lookup = new Lookup(host, addrType)
|
||||
@@ -124,8 +145,7 @@ object Utils {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
Log.e(TAG, "Failed to get interfaces' addresses.", ex)
|
||||
}
|
||||
@@ -153,8 +173,7 @@ object Utils {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
Log.e(TAG, "Failed to get interfaces' addresses.", ex)
|
||||
}
|
||||
@@ -189,19 +208,16 @@ object Utils {
|
||||
input = new BufferedReader(new InputStreamReader(p.getInputStream), 1024)
|
||||
line = input.readLine
|
||||
input.close()
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: IOException => {
|
||||
Log.e(TAG, "Unable to read sysprop " + propName, ex)
|
||||
return null
|
||||
}
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close()
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: IOException => {
|
||||
Log.e(TAG, "Exception while closing InputStream", ex)
|
||||
}
|
||||
@@ -246,7 +262,7 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
def drawableToBitmap (drawable: Drawable): Bitmap = {
|
||||
def drawableToBitmap(drawable: Drawable): Bitmap = {
|
||||
if (drawable.isInstanceOf[BitmapDrawable]) {
|
||||
return drawable.asInstanceOf[BitmapDrawable].getBitmap
|
||||
}
|
||||
@@ -271,15 +287,13 @@ object Utils {
|
||||
try {
|
||||
val appInfo: ApplicationInfo = pm.getApplicationInfo(packages(0), 0)
|
||||
appIcon = pm.getApplicationIcon(appInfo)
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
Log.e(c.getPackageName, "No package found matching with the uid " + uid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Log.e(c.getPackageName, "Package not found for uid " + uid)
|
||||
}
|
||||
appIcon
|
||||
@@ -289,8 +303,7 @@ object Utils {
|
||||
if (data_path == null) {
|
||||
if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState) {
|
||||
data_path = Environment.getExternalStorageDirectory.getAbsolutePath
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
data_path = ctx.getFilesDir.getAbsolutePath
|
||||
}
|
||||
Log.d(TAG, "Python Data Path: " + data_path)
|
||||
@@ -346,11 +359,9 @@ object Utils {
|
||||
if (isRoot != -1) return isRoot == 1
|
||||
if (new File(DEFAULT_ROOT).exists) {
|
||||
root_shell = DEFAULT_ROOT
|
||||
}
|
||||
else if (new File(ALTERNATIVE_ROOT).exists) {
|
||||
} else if (new File(ALTERNATIVE_ROOT).exists) {
|
||||
root_shell = ALTERNATIVE_ROOT
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
root_shell = "su"
|
||||
}
|
||||
val sb = new StringBuilder
|
||||
@@ -399,8 +410,7 @@ object Utils {
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
runner.join(timeout)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
runner.join()
|
||||
}
|
||||
if (runner.isAlive) {
|
||||
@@ -408,8 +418,7 @@ object Utils {
|
||||
runner.join(1000)
|
||||
return TIME_OUT
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: InterruptedException => {
|
||||
return TIME_OUT
|
||||
}
|
||||
@@ -417,28 +426,6 @@ object Utils {
|
||||
runner.exitcode
|
||||
}
|
||||
|
||||
val CLOSE_ACTION = "com.github.shadowsocks.ACTION_SHUTDOWN"
|
||||
val TAG: String = "Shadowsocks"
|
||||
val ABI_PROP: String = "ro.product.cpu.abi"
|
||||
val ABI2_PROP: String = "ro.product.cpu.abi2"
|
||||
val ARM_ABI: String = "arm"
|
||||
val X86_ABI: String = "x86"
|
||||
val MIPS_ABI: String = "mips"
|
||||
val DEFAULT_SHELL: String = "/system/bin/sh"
|
||||
val DEFAULT_ROOT: String = "/system/bin/su"
|
||||
val ALTERNATIVE_ROOT: String = "/system/xbin/su"
|
||||
val DEFAULT_IPTABLES: String = "/data/data/com.github.shadowsocks/iptables"
|
||||
val ALTERNATIVE_IPTABLES: String = "/system/bin/iptables"
|
||||
val TIME_OUT: Int = -99
|
||||
var initialized: Boolean = false
|
||||
var hasRedirectSupport: Int = -1
|
||||
var isRoot: Int = -1
|
||||
var shell: String = null
|
||||
var root_shell: String = null
|
||||
var iptables: String = null
|
||||
var data_path: String = null
|
||||
var rootTries = 0
|
||||
|
||||
/**
|
||||
* Internal thread used to execute scripts (as root or not).
|
||||
* Creates a new script runner.
|
||||
@@ -447,9 +434,13 @@ object Utils {
|
||||
* @param result result output
|
||||
* @param asroot if true, executes the script as root
|
||||
*/
|
||||
class ScriptRunner(val scripts: String,
|
||||
val result: StringBuilder,
|
||||
val asroot: Boolean) extends Thread {
|
||||
class ScriptRunner(val scripts: String, val result: StringBuilder, val asroot: Boolean)
|
||||
extends Thread {
|
||||
|
||||
var exitcode: Int = -1
|
||||
val pid: Array[Int] = new Array[Int](1)
|
||||
var pipe: FileDescriptor = null
|
||||
|
||||
override def destroy() {
|
||||
if (pid(0) != -1) {
|
||||
Exec.hangupProcessGroup(pid(0))
|
||||
@@ -464,7 +455,9 @@ object Utils {
|
||||
def createSubprocess(processId: Array[Int], cmd: String): FileDescriptor = {
|
||||
val args = parse(cmd)
|
||||
val arg0 = args(0)
|
||||
Exec.createSubprocess(if (result != null) 1 else 0, arg0, args, null, scripts + "\nexit\n", processId)
|
||||
Exec
|
||||
.createSubprocess(if (result != null) 1 else 0, arg0, args, null, scripts + "\nexit\n",
|
||||
processId)
|
||||
}
|
||||
|
||||
def parse(cmd: String): Array[String] = {
|
||||
@@ -515,8 +508,7 @@ object Utils {
|
||||
try {
|
||||
if (this.asroot) {
|
||||
pipe = createSubprocess(pid, root_shell)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pipe = createSubprocess(pid, getShell)
|
||||
}
|
||||
if (pid(0) != -1) {
|
||||
@@ -530,14 +522,12 @@ object Utils {
|
||||
read = stdout.read(buf)
|
||||
result.append(new String(buf, 0, read))
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
Log.e(TAG, "Cannot execute command", ex)
|
||||
if (result != null) result.append("\n").append(ex)
|
||||
}
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
if (pipe != null) {
|
||||
Exec.close(pipe)
|
||||
}
|
||||
@@ -546,10 +536,6 @@ object Utils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var exitcode: Int = -1
|
||||
val pid: Array[Int] = new Array[Int](1)
|
||||
var pipe: FileDescriptor = null
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user