Compare commits

...
18 Commits
Author SHA1 Message Date
Max Lv 3db8e9d775 refine #171 2014-12-09 21:04:54 +08:00
Max Lv e708e117be fix #171 2014-12-09 20:59:04 +08:00
Max Lv 836088534b bump version 2014-12-09 17:37:58 +08:00
Max Lv 6ae97e630e fix #170 2014-12-09 17:20:29 +08:00
Max Lv bae0b9e746 fix a bug 2014-12-08 14:53:30 +08:00
Max Lv 15262343f4 restore DNS nicely 2014-12-08 10:35:59 +08:00
Max Lv be31a0ce82 bump version 2014-12-06 19:16:08 +08:00
Max Lv 0b0b6137b8 disable NAT mode on lollipop by default 2014-12-06 19:15:41 +08:00
Max Lv 156b0c27f5 refine DNS settings 2014-12-06 11:54:42 +08:00
Max Lv ca99596270 Merge branch 'master' of github.com:shadowsocks/shadowsocks-android
Conflicts:
	src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
2014-12-06 11:30:07 +08:00
Max Lv e22ffa8601 improve DNS utils 2014-12-06 11:29:11 +08:00
Max Lv 24233f104a Fix #167 2014-12-04 19:21:22 +08:00
Max Lv 01306d527d Refine DNS settings 2014-12-02 15:36:27 +08:00
Max Lv 5547b42283 fix some DNS issues 2014-12-02 14:58:17 +08:00
Max Lv 3dba966355 fix two potential FCs 2014-12-01 14:04:21 +08:00
Max Lv c9a7dc8deb bump version 2014-12-01 10:56:03 +08:00
Max Lv 4df7634fae fix a FC 2014-11-30 21:26:57 +08:00
Max Lv 97dd8fa15e fix #132 2014-11-30 10:18:27 +08:00
14 changed files with 235 additions and 116 deletions
+2 -2
View File
@@ -20,9 +20,9 @@ resolvers += "madeye private releases" at "http://madeye-maven-repository.google
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"dnsjava" % "dnsjava" % "2.1.5", "dnsjava" % "dnsjava" % "2.1.5",
"org.scalaj" %% "scalaj-http" % "0.3.10", "com.github.kevinsawicki" % "http-request" % "5.6",
"commons-net" % "commons-net" % "3.3", "commons-net" % "commons-net" % "3.3",
"com.google.zxing" % "android-integration" % "2.2" "com.google.zxing" % "android-integration" % "3.1.0"
) )
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
+2 -2
View File
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks" package="com.github.shadowsocks"
android:versionCode="88" android:versionCode="91"
android:versionName="2.5.0"> android:versionName="2.5.3">
<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"/>
+4
View File
@@ -46,6 +46,7 @@
<string name="forward_stop">后台服务已停止。</string> <string name="forward_stop">后台服务已停止。</string>
<string name="service_failed">无法连接远程服务器</string> <string name="service_failed">无法连接远程服务器</string>
<string name="stop">停止服务</string> <string name="stop">停止服务</string>
<string name="stopping">正在关闭...</string>
<string name="vpn_error">后台服务启动失败:\"%s\"。</string> <string name="vpn_error">后台服务启动失败:\"%s\"。</string>
<string name="ok_iknow">确定</string> <string name="ok_iknow">确定</string>
<string name="port_alert">端口号应大于1024</string> <string name="port_alert">端口号应大于1024</string>
@@ -68,6 +69,9 @@
<string name="flushing">刷新中...</string> <string name="flushing">刷新中...</string>
<string name="add_profile">添加配置文件</string> <string name="add_profile">添加配置文件</string>
<string name="enable_nat">启用 NAT 模式.</string>
<string name="disable_nat">禁用 NAT 模式.</string>
<string name="udp_dns">UDP 转发</string> <string name="udp_dns">UDP 转发</string>
<string name="udp_dns_summary">由远程服务器转发 UDP 协议的数据包</string> <string name="udp_dns_summary">由远程服务器转发 UDP 协议的数据包</string>
File diff suppressed because one or more lines are too long
+3
View File
@@ -57,7 +57,10 @@
<string name="forward_stop">Shadowsocks stopped.</string> <string name="forward_stop">Shadowsocks stopped.</string>
<string name="service_failed">Failed to connect the remote server</string> <string name="service_failed">Failed to connect the remote server</string>
<string name="stop">Stop the service</string> <string name="stop">Stop the service</string>
<string name="stopping">Shutting down...</string>
<string name="vpn_error">Error: \"%s\".</string> <string name="vpn_error">Error: \"%s\".</string>
<string name="enable_nat">Turn on NAT mode.</string>
<string name="disable_nat">Turn off NAT mode.</string>
<!-- alert category --> <!-- alert category -->
<string name="ok_iknow">OK</string> <string name="ok_iknow">OK</string>
@@ -46,7 +46,6 @@ import android.content.pm.PackageManager
import android.graphics.{Bitmap, PixelFormat} import android.graphics.{Bitmap, PixelFormat}
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Message
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.view.View import android.view.View
import android.view.View.OnClickListener import android.view.View.OnClickListener
@@ -62,7 +61,10 @@ import com.nostra13.universalimageloader.core.{DisplayImageOptions, ImageLoader,
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
import com.github.shadowsocks.utils.{Utils, Scheme, Key} import com.github.shadowsocks.utils.{Utils, Scheme, Key}
case class ProxiedApp(uid: Int, name: String, var proxied: Boolean) import scala.concurrent.ops._
import scala.language.implicitConversions
case class ProxiedApp(uid: Int, name: String, packageName: String, var proxied: Boolean)
class ObjectArrayTools[T <: AnyRef](a: Array[T]) { class ObjectArrayTools[T <: AnyRef](a: Array[T]) {
def binarySearch(key: T) = { def binarySearch(key: T) = {
@@ -74,7 +76,7 @@ case class ListEntry(box: CheckBox, text: TextView, icon: ImageView)
object AppManager { object AppManager {
implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]) = new ObjectArrayTools(a) implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]): ObjectArrayTools[T] = new ObjectArrayTools(a)
def getProxiedApps(context: Context, proxiedAppString: String): Array[ProxiedApp] = { def getProxiedApps(context: Context, proxiedAppString: String): Array[ProxiedApp] = {
@@ -86,13 +88,13 @@ object AppManager {
val appList = packageManager.getInstalledApplications(0) val appList = packageManager.getInstalledApplications(0)
appList.filter(_.uid >= 10000).map { appList.filter(_.uid >= 10000).map {
case a => { case a =>
val uid = a.uid val uid = a.uid
val userName = uid.toString val userName = uid.toString
val name = packageManager.getApplicationLabel(a).toString
val packageName = a.packageName val packageName = a.packageName
val proxied = proxiedApps.binarySearch(userName) >= 0 val proxied = proxiedApps.binarySearch(userName) >= 0
new ProxiedApp(uid, packageName, proxied) new ProxiedApp(uid, name, packageName, proxied)
}
}.toArray }.toArray
} }
} }
@@ -103,7 +105,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
val MSG_LOAD_FINISH = 2 val MSG_LOAD_FINISH = 2
val STUB = android.R.drawable.sym_def_app_icon val STUB = android.R.drawable.sym_def_app_icon
implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]) = new ObjectArrayTools(a) implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]): ObjectArrayTools[T] = new ObjectArrayTools(a)
var apps: Array[ProxiedApp] = null var apps: Array[ProxiedApp] = null
var appListView: ListView = null var appListView: ListView = null
@@ -129,8 +131,9 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
val uid = a.uid val uid = a.uid
val userName = uid.toString val userName = uid.toString
val name = packageManager.getApplicationLabel(a).toString val name = packageManager.getApplicationLabel(a).toString
val packageName = a.packageName
val proxied = (proxiedApps binarySearch userName) >= 0 val proxied = (proxiedApps binarySearch userName) >= 0
new ProxiedApp(uid, name, proxied) new ProxiedApp(uid, name, packageName, proxied)
}.toArray }.toArray
} }
@@ -175,7 +178,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
.cacheOnDisc() .cacheOnDisc()
.displayer(new FadeInBitmapDisplayer(300)) .displayer(new FadeInBitmapDisplayer(300))
.build() .build()
ImageLoader.getInstance().displayImage(Scheme.APP + app.uid, entry.icon, options) ImageLoader.getInstance().displayImage(Scheme.APP + app.packageName, entry.icon, options)
entry.text.setText(app.name) entry.text.setText(app.name)
val box: CheckBox = entry.box val box: CheckBox = entry.box
@@ -243,18 +246,17 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
} }
}) })
bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false)) bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false))
appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
} }
protected override def onResume() { protected override def onResume() {
super.onResume() super.onResume()
new Thread { spawn {
override def run() { handler.post(loadStartRunnable)
handler.post(loadStartRunnable) if (!appsLoaded) loadApps()
appListView = findViewById(R.id.applistview).asInstanceOf[ListView] handler.post(loadFinishRunnable)
if (!appsLoaded) loadApps() }
handler.post(loadFinishRunnable)
}
}.start()
} }
def saveAppSettings(context: Context) { def saveAppSettings(context: Context) {
@@ -319,8 +321,8 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
} }
override def getStreamFromOtherSource(imageUri: String, extra: AnyRef): InputStream = { override def getStreamFromOtherSource(imageUri: String, extra: AnyRef): InputStream = {
val uid = imageUri.substring(Scheme.APP.length).toInt val packageName = imageUri.substring(Scheme.APP.length)
val drawable = Utils.getAppIcon(getBaseContext, uid) val drawable = Utils.getAppIcon(getBaseContext, packageName)
val bitmap = Utils.drawableToBitmap(drawable) val bitmap = Utils.drawableToBitmap(drawable)
val os = new ByteArrayOutputStream() val os = new ByteArrayOutputStream()
@@ -81,11 +81,15 @@ trait BaseService {
} }
override def stop() { override def stop() {
stopRunner() if (state != State.CONNECTING && state != State.STOPPING) {
stopRunner()
}
} }
override def start(config: Config) { override def start(config: Config) {
startRunner(config) if (state != State.CONNECTING && state != State.STOPPING) {
startRunner(config)
}
} }
} }
@@ -40,6 +40,7 @@ package com.github.shadowsocks
import java.util import java.util
import java.io.{OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream, IOException, FileOutputStream} import java.io.{OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream, IOException, FileOutputStream}
import java.util.Locale
import android.app.backup.BackupManager import android.app.backup.BackupManager
import android.app.{Activity, AlertDialog, ProgressDialog} import android.app.{Activity, AlertDialog, ProgressDialog}
@@ -51,6 +52,7 @@ import android.net.{Uri, VpnService}
import android.os._ import android.os._
import android.preference._ import android.preference._
import android.util.{DisplayMetrics, Log} import android.util.{DisplayMetrics, Log}
import android.view.View.OnLongClickListener
import android.view._ import android.view._
import android.webkit.{WebView, WebViewClient} import android.webkit.{WebView, WebViewClient}
import android.widget._ import android.widget._
@@ -67,7 +69,6 @@ import net.simonvt.menudrawer.MenuDrawer
import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import scala.concurrent.ops._ import scala.concurrent.ops._
import scala.ref.WeakReference
class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int) class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int)
extends BaseImageDownloader(context, connectTimeout, readTimeout) { extends BaseImageDownloader(context, connectTimeout, readTimeout) {
@@ -325,8 +326,8 @@ class Shadowsocks
val cmd = new ArrayBuffer[String]() val cmd = new ArrayBuffer[String]()
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) { for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) {
cmd.append("chmod 666 %s%s-nat.pid".format(Path.BASE, task)) cmd.append("chmod 666 %s%s-nat.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("chmod 666 %s%s-vpn.pid".format(Path.BASE, task)) cmd.append("chmod 666 %s%s-vpn.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
} }
Console.runRootCommand(cmd.toArray) Console.runRootCommand(cmd.toArray)
cmd.clear() cmd.clear()
@@ -335,17 +336,17 @@ class Shadowsocks
try { try {
val pid_nat = scala.io.Source.fromFile(Path.BASE + task + "-nat.pid").mkString.trim.toInt val pid_nat = scala.io.Source.fromFile(Path.BASE + task + "-nat.pid").mkString.trim.toInt
val pid_vpn = scala.io.Source.fromFile(Path.BASE + task + "-vpn.pid").mkString.trim.toInt val pid_vpn = scala.io.Source.fromFile(Path.BASE + task + "-vpn.pid").mkString.trim.toInt
cmd.append("kill -9 %d".format(pid_nat)) cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_nat))
cmd.append("kill -9 %d".format(pid_vpn)) cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_vpn))
Process.killProcess(pid_nat) Process.killProcess(pid_nat)
Process.killProcess(pid_vpn) Process.killProcess(pid_vpn)
} catch { } catch {
case e: Throwable => Log.e(Shadowsocks.TAG, "unable to kill " + task, e) case e: Throwable => Log.e(Shadowsocks.TAG, "unable to kill " + task)
} }
cmd.append("rm -f %s%s-nat.pid".format(Path.BASE, task)) cmd.append("rm -f %s%s-nat.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s%s-nat.conf".format(Path.BASE, task)) cmd.append("rm -f %s%s-nat.conf".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s%s-vpn.pid".format(Path.BASE, task)) cmd.append("rm -f %s%s-vpn.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s%s-vpn.conf".format(Path.BASE, task)) cmd.append("rm -f %s%s-vpn.conf".formatLocal(Locale.ENGLISH, Path.BASE, task))
} }
Console.runCommand(cmd.toArray) Console.runCommand(cmd.toArray)
Console.runRootCommand(cmd.toArray) Console.runRootCommand(cmd.toArray)
@@ -499,13 +500,30 @@ class Shadowsocks
getActionBar.setDisplayShowTitleEnabled(false) getActionBar.setDisplayShowTitleEnabled(false)
getActionBar.setDisplayShowCustomEnabled(true) getActionBar.setDisplayShowCustomEnabled(true)
getActionBar.setIcon(R.drawable.ic_stat_shadowsocks) getActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
title.setOnLongClickListener(new OnLongClickListener {
override def onLongClick(v: View): Boolean = {
if (Utils.isLollipopOrAbove && bgService != null
&& (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) {
val natEnabled = status.getBoolean(Key.isNAT, false)
status.edit().putBoolean(Key.isNAT, !natEnabled).commit()
if (!natEnabled) {
Toast.makeText(getBaseContext, R.string.enable_nat, Toast.LENGTH_LONG).show()
} else {
Toast.makeText(getBaseContext, R.string.disable_nat, Toast.LENGTH_LONG).show()
}
true
} else {
false
}
}
})
// Register broadcast receiver // Register broadcast receiver
registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS)) registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS))
// Bind to the service // Bind to the service
spawn { spawn {
val isRoot = Console.isRoot val isRoot = (!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot
handler.post(new Runnable { handler.post(new Runnable {
override def run() { override def run() {
status.edit.putBoolean(Key.isRoot, isRoot).commit() status.edit.putBoolean(Key.isRoot, isRoot).commit()
@@ -575,6 +593,9 @@ class Shadowsocks
h.postDelayed(new Runnable() { h.postDelayed(new Runnable() {
def run() { def run() {
val integrator = new IntentIntegrator(Shadowsocks.this) val integrator = new IntentIntegrator(Shadowsocks.this)
val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN)
list.add("tw.com.quickmark")
integrator.setTargetApplications(list)
integrator.initiateScan() integrator.initiateScan()
h.sendEmptyMessage(0) h.sendEmptyMessage(0)
} }
@@ -652,7 +673,7 @@ class Shadowsocks
if (!profile.isDefined) return false if (!profile.isDefined) return false
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setMessage(String.format(getString(R.string.remove_profile), profile.get.name)) .setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), profile.get.name))
.setCancelable(false) .setCancelable(false)
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, i: Int) = dialog.cancel() override def onClick(dialog: DialogInterface, i: Int) = dialog.cancel()
@@ -820,7 +841,7 @@ class Shadowsocks
val ab = new ArrayBuffer[String] val ab = new ArrayBuffer[String]
ab.append("mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system") ab.append("mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system")
for (executable <- Shadowsocks.EXECUTABLES) { for (executable <- Shadowsocks.EXECUTABLES) {
ab.append("cp %s%s /system/bin/".format(Path.BASE, executable)) ab.append("cp %s%s /system/bin/".formatLocal(Locale.ENGLISH, Path.BASE, executable))
ab.append("chmod 755 /system/bin/" + executable) ab.append("chmod 755 /system/bin/" + executable)
ab.append("chown root:shell /system/bin/" + executable) ab.append("chown root:shell /system/bin/" + executable)
} }
@@ -956,7 +977,7 @@ class Shadowsocks
} }
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(getString(R.string.about_title).format(versionName)) .setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, versionName))
.setCancelable(false) .setCancelable(false)
.setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener() { .setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) { override def onClick(dialog: DialogInterface, id: Int) {
@@ -1001,11 +1022,16 @@ class Shadowsocks
.setDuration(Configuration.DURATION_LONG) .setDuration(Configuration.DURATION_LONG)
.build() .build()
Crouton Crouton
.makeText(Shadowsocks.this, getString(R.string.vpn_error).format(m), style) .makeText(Shadowsocks.this, getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m), style)
.setConfiguration(config) .setConfiguration(config)
.show() .show()
} }
setPreferenceEnabled(enabled = true) setPreferenceEnabled(enabled = true)
case State.STOPPING =>
if (progressDialog == null) {
progressDialog = ProgressDialog
.show(Shadowsocks.this, "", getString(R.string.stopping), true, true)
}
} }
} }
} }
@@ -41,6 +41,7 @@ package com.github.shadowsocks
import java.io.File import java.io.File
import java.lang.reflect.{InvocationTargetException, Method} import java.lang.reflect.{InvocationTargetException, Method}
import java.util.Locale
import android.app.{Notification, NotificationManager, PendingIntent, Service} import android.app.{Notification, NotificationManager, PendingIntent, Service}
import android.content._ import android.content._
@@ -48,7 +49,8 @@ import android.content.pm.{PackageInfo, PackageManager}
import android.net.{Network, ConnectivityManager} import android.net.{Network, ConnectivityManager}
import android.os._ import android.os._
import android.support.v4.app.NotificationCompat import android.support.v4.app.NotificationCompat
import android.util.Log import android.util.{SparseArray, Log}
import android.widget.Toast
import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders import com.google.android.gms.analytics.HitBuilders
@@ -70,9 +72,12 @@ class ShadowsocksNatService extends Service with BaseService {
private val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification]) private val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
private val mStopForegroundSignature = Array[Class[_]](classOf[Boolean]) private val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundSignature = Array[Class[_]](classOf[Boolean]) private val mSetForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundArgs = new Array[AnyRef](1)
private val mStartForegroundArgs = new Array[AnyRef](2)
private val mStopForegroundArgs = new Array[AnyRef](1)
var closeReceiver: BroadcastReceiver = null var closeReceiver: BroadcastReceiver = null
var conReceiver: BroadcastReceiver = null var connReceiver: BroadcastReceiver = null
var notificationManager: NotificationManager = null var notificationManager: NotificationManager = null
var config: Config = null var config: Config = null
var apps: Array[ProxiedApp] = null var apps: Array[ProxiedApp] = null
@@ -81,51 +86,98 @@ class ShadowsocksNatService extends Service with BaseService {
private var mSetForeground: Method = null private var mSetForeground: Method = null
private var mStartForeground: Method = null private var mStartForeground: Method = null
private var mStopForeground: Method = null private var mStopForeground: Method = null
private var mSetForegroundArgs = new Array[AnyRef](1)
private var mStartForegroundArgs = new Array[AnyRef](2)
private var mStopForegroundArgs = new Array[AnyRef](1)
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication] private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
def setDnsForAllNetwork(dns: String) { private val dnsAddressCache = new SparseArray[String]
def getNetId(network: Network): Int = {
network.getClass.getDeclaredField("netId").get(network).asInstanceOf[Int]
}
def restoreDnsForAllNetwork() {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager] val manager = getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager]
val networks = manager.getAllNetworks val networks = manager.getAllNetworks
val cmdBuf = new ArrayBuffer[String]() val cmdBuf = new ArrayBuffer[String]()
networks.foreach(network => { networks.foreach(network => {
val networkInfo = manager.getNetworkInfo(network) val netId = getNetId(network)
if (networkInfo.isAvailable) { val oldDns = dnsAddressCache.get(netId)
val netId = network.getClass.getDeclaredField("netId").get(network).asInstanceOf[Int] if (oldDns != null) {
cmdBuf.append("ndc resolver setnetdns %d \"\" %s".format(netId, dns)) cmdBuf.append("ndc resolver setnetdns %d \"\" %s".formatLocal(Locale.ENGLISH, netId, oldDns))
dnsAddressCache.remove(netId)
} }
}) })
Console.runRootCommand(cmdBuf.toArray) if (cmdBuf.nonEmpty) Console.runRootCommand(cmdBuf.toArray)
} }
def setupDns() = { def setDnsForAllNetwork(dns: String) {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager]
val networks = manager.getAllNetworks
if (networks == null) return
val cmdBuf = new ArrayBuffer[String]()
networks.foreach(network => {
val networkInfo = manager.getNetworkInfo(network)
if (networkInfo == null) return
if (networkInfo.isConnected) {
val netId = getNetId(network)
val curDnsList = manager.getLinkProperties(network).getDnsServers
if (curDnsList != null) {
import scala.collection.JavaConverters._
val curDns = curDnsList.asScala.map(ip => ip.getHostAddress).mkString(" ")
if (curDns != dns) {
dnsAddressCache.put(netId, curDns)
cmdBuf.append("ndc resolver setnetdns %d \"\" %s".formatLocal(Locale.ENGLISH, netId, dns))
}
}
}
})
if (cmdBuf.nonEmpty) Console.runRootCommand(cmdBuf.toArray)
}
def setupDns() {
setDnsForAllNetwork("127.0.0.1") setDnsForAllNetwork("127.0.0.1")
} }
def resetDns() = { def resetDns() = {
setDnsForAllNetwork("8.8.8.8 208.67.222.222 114.114.114.114") restoreDnsForAllNetwork()
} }
def flushDns() {
if (Utils.isLollipopOrAbove) {
val manager = getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager]
val networks = manager.getAllNetworks
val cmdBuf = new ArrayBuffer[String]()
networks.foreach(network => {
val networkInfo = manager.getNetworkInfo(network)
if (networkInfo.isAvailable) {
val netId = network.getClass.getDeclaredField("netId").get(network).asInstanceOf[Int]
cmdBuf.append("ndc resolver flushnet %d".formatLocal(Locale.ENGLISH, netId))
}
})
Console.runRootCommand(cmdBuf.toArray)
} else {
Console.runRootCommand(Array("ndc resolver flushdefaultif", "ndc resolver flushif wlan0"))
}
}
def destroyConnectionReceiver() { def destroyConnectionReceiver() {
if (conReceiver != null) { if (connReceiver != null) {
unregisterReceiver(conReceiver) unregisterReceiver(connReceiver)
conReceiver = null connReceiver = null
} }
resetDns() resetDns()
} }
def initConnectionReceiver() { def initConnectionReceiver() {
setupDns()
val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
conReceiver = new BroadcastReceiver { connReceiver = new BroadcastReceiver {
override def onReceive(context: Context, intent: Intent) = { override def onReceive(context: Context, intent: Intent) = {
setupDns() setupDns()
} }
} }
registerReceiver(conReceiver, filter) registerReceiver(connReceiver, filter)
} }
def startShadowsocksDaemon() { def startShadowsocksDaemon() {
@@ -140,7 +192,7 @@ class ShadowsocksNatService extends Service with BaseService {
} }
val conf = ConfigUtils val conf = ConfigUtils
.SHADOWSOCKS.format(config.proxy, config.remotePort, config.localPort, .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
config.sitekey, config.encMethod, 10) config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-nat.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "ss-local-nat.conf"))(p => {
p.println(conf) p.println(conf)
@@ -164,7 +216,7 @@ class ShadowsocksNatService extends Service with BaseService {
def startTunnel() { def startTunnel() {
if (config.isUdpDns) { if (config.isUdpDns) {
val conf = ConfigUtils val conf = ConfigUtils
.SHADOWSOCKS.format(config.proxy, config.remotePort, 8153, .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8153,
config.sitekey, config.encMethod, 10) config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-nat.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-nat.conf"))(p => {
p.println(conf) p.println(conf)
@@ -192,7 +244,7 @@ class ShadowsocksNatService extends Service with BaseService {
} else { } else {
val conf = ConfigUtils val conf = ConfigUtils
.SHADOWSOCKS.format(config.proxy, config.remotePort, 8163, .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
config.sitekey, config.encMethod, 10) config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-nat.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-nat.conf"))(p => {
p.println(conf) p.println(conf)
@@ -213,10 +265,10 @@ class ShadowsocksNatService extends Service with BaseService {
def startDnsDaemon() { def startDnsDaemon() {
val conf = if (Utils.isLollipopOrAbove) { val conf = if (Utils.isLollipopOrAbove) {
ConfigUtils ConfigUtils
.PDNSD_BYPASS.format("127.0.0.1", 53, Path.BASE + "pdnsd-nat.pid", getString(R.string.exclude), 8163) .PDNSD_BYPASS.formatLocal(Locale.ENGLISH, "127.0.0.1", 53, Path.BASE + "pdnsd-nat.pid", getString(R.string.exclude), 8163)
} else { } else {
ConfigUtils ConfigUtils
.PDNSD_BYPASS.format("127.0.0.1", 8153, Path.BASE + "pdnsd-nat.pid", getString(R.string.exclude), 8163) .PDNSD_BYPASS.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153, Path.BASE + "pdnsd-nat.pid", getString(R.string.exclude), 8163)
} }
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-nat.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-nat.conf"))(p => {
p.println(conf) p.println(conf)
@@ -246,9 +298,9 @@ class ShadowsocksNatService extends Service with BaseService {
} }
def startRedsocksDaemon() { def startRedsocksDaemon() {
val conf = ConfigUtils.REDSOCKS.format(config.localPort) val conf = ConfigUtils.REDSOCKS.formatLocal(Locale.ENGLISH, config.localPort)
val cmd = Path.BASE + "redsocks -p %sredsocks-nat.pid -c %sredsocks-nat.conf" val cmd = Path.BASE + "redsocks -p %sredsocks-nat.pid -c %sredsocks-nat.conf"
.format(Path.BASE, Path.BASE) .formatLocal(Locale.ENGLISH, Path.BASE, Path.BASE)
ConfigUtils.printToFile(new File(Path.BASE + "redsocks-nat.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "redsocks-nat.conf"))(p => {
p.println(conf) p.println(conf)
}) })
@@ -265,7 +317,6 @@ class ShadowsocksNatService extends Service with BaseService {
startRedsocksDaemon() startRedsocksDaemon()
startShadowsocksDaemon() startShadowsocksDaemon()
setupIptables() setupIptables()
flushDNS()
true true
} }
@@ -342,7 +393,7 @@ class ShadowsocksNatService extends Service with BaseService {
val cmd = new ArrayBuffer[String]() val cmd = new ArrayBuffer[String]()
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks")) { for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks")) {
cmd.append("chmod 666 %s%s-nat.pid".format(Path.BASE, task)) cmd.append("chmod 666 %s%s-nat.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
} }
Console.runRootCommand(cmd.toArray) Console.runRootCommand(cmd.toArray)
cmd.clear() cmd.clear()
@@ -350,23 +401,19 @@ class ShadowsocksNatService extends Service with BaseService {
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks")) { for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks")) {
try { try {
val pid = scala.io.Source.fromFile(Path.BASE + task + "-nat.pid").mkString.trim.toInt val pid = scala.io.Source.fromFile(Path.BASE + task + "-nat.pid").mkString.trim.toInt
cmd.append("kill -9 %d".format(pid)) cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid))
Process.killProcess(pid) Process.killProcess(pid)
} catch { } catch {
case e: Throwable => Log.e(TAG, "unable to kill " + task, e) case e: Throwable => Log.e(TAG, "unable to kill " + task)
} }
cmd.append("rm -f %s%s-nat.pid".format(Path.BASE, task)) cmd.append("rm -f %s%s-nat.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s%s-nat.conf".format(Path.BASE, task)) cmd.append("rm -f %s%s-nat.conf".formatLocal(Locale.ENGLISH, Path.BASE, task))
} }
Console.runRootCommand(cmd.toArray) Console.runRootCommand(cmd.toArray)
Console.runRootCommand(Utils.getIptables + " -t nat -F OUTPUT") Console.runRootCommand(Utils.getIptables + " -t nat -F OUTPUT")
} }
def flushDNS() {
Console.runRootCommand(Array("ndc resolver flushdefaultif", "ndc resolver flushif wlan0"))
}
def setupIptables() = { def setupIptables() = {
val init_sb = new ArrayBuffer[String] val init_sb = new ArrayBuffer[String]
val http_sb = new ArrayBuffer[String] val http_sb = new ArrayBuffer[String]
@@ -453,20 +500,21 @@ class ShadowsocksNatService extends Service with BaseService {
config = c config = c
// register connection receiver
if (Utils.isLollipopOrAbove) initConnectionReceiver()
// register close receiver // register close receiver
val filter = new IntentFilter() val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE) filter.addAction(Action.CLOSE)
closeReceiver = new BroadcastReceiver() { closeReceiver = new BroadcastReceiver() {
def onReceive(p1: Context, p2: Intent) { def onReceive(p1: Context, p2: Intent) {
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner() stopRunner()
} }
} }
registerReceiver(closeReceiver, filter) registerReceiver(closeReceiver, filter)
// register connection receiver
if (Utils.isLollipopOrAbove) initConnectionReceiver()
// send event // send event
application.tracker.send(new HitBuilders.EventBuilder() application.tracker.send(new HitBuilders.EventBuilder()
.setCategory(TAG) .setCategory(TAG)
@@ -492,6 +540,7 @@ class ShadowsocksNatService extends Service with BaseService {
if (config != null) { if (config != null) {
// Clean up
killProcesses() killProcesses()
var resolved: Boolean = false var resolved: Boolean = false
@@ -508,8 +557,13 @@ class ShadowsocksNatService extends Service with BaseService {
} }
if (resolved && handleConnection) { if (resolved && handleConnection) {
// Set DNS
if (Utils.isLollipopOrAbove) setupDns()
flushDns()
notifyForegroundAlert(getString(R.string.forward_success), notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).format(config.profileName)) getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName))
changeState(State.CONNECTED) changeState(State.CONNECTED)
} else { } else {
changeState(State.STOPPED, getString(R.string.service_failed)) changeState(State.STOPPED, getString(R.string.service_failed))
@@ -521,6 +575,9 @@ class ShadowsocksNatService extends Service with BaseService {
override def stopRunner() { override def stopRunner() {
// channge the state
changeState(State.STOPPING)
// clean up recevier // clean up recevier
if (closeReceiver != null) { if (closeReceiver != null) {
unregisterReceiver(closeReceiver) unregisterReceiver(closeReceiver)
@@ -50,7 +50,8 @@ import com.github.shadowsocks.aidl.IShadowsocksService
class ShadowsocksRunnerActivity extends Activity { class ShadowsocksRunnerActivity extends Activity {
lazy val settings = PreferenceManager.getDefaultSharedPreferences(this) private lazy val settings = PreferenceManager.getDefaultSharedPreferences(this)
private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
val handler = new Handler() val handler = new Handler()
val connection = new ServiceConnection { val connection = new ServiceConnection {
@@ -72,10 +73,10 @@ class ShadowsocksRunnerActivity extends Activity {
def isVpnEnabled: Boolean = { def isVpnEnabled: Boolean = {
if (vpnEnabled < 0) { if (vpnEnabled < 0) {
vpnEnabled = if (!Console.isRoot) { vpnEnabled = if ((!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot) {
1
} else {
0 0
} else {
1
} }
} }
if (vpnEnabled == 1) true else false if (vpnEnabled == 1) true else false
@@ -40,6 +40,7 @@
package com.github.shadowsocks package com.github.shadowsocks
import java.io.File import java.io.File
import java.util.Locale
import android.app._ import android.app._
import android.content._ import android.content._
@@ -47,6 +48,7 @@ import android.content.pm.{PackageInfo, PackageManager}
import android.net.VpnService import android.net.VpnService
import android.os._ import android.os._
import android.util.Log import android.util.Log
import android.widget.Toast
import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders import com.google.android.gms.analytics.HitBuilders
@@ -106,7 +108,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
} }
val conf = ConfigUtils val conf = ConfigUtils
.SHADOWSOCKS.format(config.proxy, config.remotePort, config.localPort, .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
config.sitekey, config.encMethod, 10) config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => {
p.println(conf) p.println(conf)
@@ -129,7 +131,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
def startDnsTunnel() = { def startDnsTunnel() = {
val conf = ConfigUtils val conf = ConfigUtils
.SHADOWSOCKS.format(config.proxy, config.remotePort, 8163, .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
config.sitekey, config.encMethod, 10) config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-vpn.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-vpn.conf"))(p => {
p.println(conf) p.println(conf)
@@ -149,9 +151,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
def startDnsDaemon() { def startDnsDaemon() {
val conf = { val conf = {
if (Utils.isLollipopOrAbove) { if (Utils.isLollipopOrAbove) {
ConfigUtils.PDNSD_BYPASS.format("0.0.0.0", 8153, Path.BASE + "pdnsd-vpn.pid", getString(R.string.exclude), 8163) ConfigUtils.PDNSD_BYPASS.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153, Path.BASE + "pdnsd-vpn.pid", getString(R.string.exclude), 8163)
} else { } else {
ConfigUtils.PDNSD_LOCAL.format("0.0.0.0", 8163) ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8163)
} }
} }
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => { ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => {
@@ -181,7 +183,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
builder builder
.setSession(config.profileName) .setSession(config.profileName)
.setMtu(VPN_MTU) .setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN.format("1"), 24) .addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
.addDnsServer("8.8.8.8") .addDnsServer("8.8.8.8")
if (Utils.isLollipopOrAbove) { if (Utils.isLollipopOrAbove) {
@@ -190,7 +192,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String] val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String]
for (app <- apps) { for (app <- apps) {
if (app.proxied) { if (app.proxied) {
pkgSet.add(app.name) pkgSet.add(app.packageName)
} }
} }
for (pkg <- pkgSet) { for (pkg <- pkgSet) {
@@ -311,12 +313,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
+ "--tunmtu %d " + "--tunmtu %d "
+ "--loglevel 3 " + "--loglevel 3 "
+ "--pid %stun2socks-vpn.pid") + "--pid %stun2socks-vpn.pid")
.format(PRIVATE_VLAN.format("2"), config.localPort, fd, VPN_MTU, Path.BASE) .formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"), config.localPort, fd, VPN_MTU, Path.BASE)
if (config.isUdpDns) if (config.isUdpDns)
cmd += " --enable-udprelay" cmd += " --enable-udprelay"
else else
cmd += " --dnsgw %s:8153".format(PRIVATE_VLAN.format("1")) cmd += " --dnsgw %s:8153".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"))
if (Utils.isLollipopOrAbove) { if (Utils.isLollipopOrAbove) {
cmd += " --fake-proc" cmd += " --fake-proc"
@@ -367,7 +369,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val pid = scala.io.Source.fromFile(Path.BASE + task + "-vpn.pid").mkString.trim.toInt val pid = scala.io.Source.fromFile(Path.BASE + task + "-vpn.pid").mkString.trim.toInt
Process.killProcess(pid) Process.killProcess(pid)
} catch { } catch {
case e: Throwable => Log.e(TAG, "unable to kill " + task, e) case e: Throwable => Log.e(TAG, "unable to kill " + task)
} }
} }
} }
@@ -396,6 +398,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Intent.ACTION_SHUTDOWN)
receiver = new BroadcastReceiver { receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) { def onReceive(p1: Context, p2: Intent) {
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT)
stopRunner() stopRunner()
} }
} }
@@ -448,7 +451,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
override def stopRunner() { override def stopRunner() {
// channge the state // channge the state
changeState(State.STOPPED) changeState(State.STOPPING)
// send event // send event
application.tracker.send(new HitBuilders.EventBuilder() application.tracker.send(new HitBuilders.EventBuilder()
@@ -476,6 +479,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
unregisterReceiver(receiver) unregisterReceiver(receiver)
receiver = null receiver = null
} }
// channge the state
changeState(State.STOPPED)
} }
override def stopBackgroundService() { override def stopBackgroundService() {
@@ -42,7 +42,7 @@ package com.github.shadowsocks.utils
import android.content.{SharedPreferences, Context} import android.content.{SharedPreferences, Context}
import com.github.shadowsocks.ShadowsocksApplication import com.github.shadowsocks.ShadowsocksApplication
import com.google.android.gms.tagmanager.Container import com.google.android.gms.tagmanager.Container
import scalaj.http.{HttpOptions, Http} import com.github.kevinsawicki.http.HttpRequest
import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.aidl.Config
object ConfigUtils { object ConfigUtils {
@@ -183,12 +183,12 @@ object ConfigUtils {
def getPublicConfig(context: Context, container: Container, config: Config): Config = { def getPublicConfig(context: Context, container: Container, config: Config): Config = {
val url = container.getString("proxy_url") val url = container.getString("proxy_url")
val sig = Utils.getSignature(context) val sig = Utils.getSignature(context)
val list = Http val list = HttpRequest
.post(url) .post(url)
.params("sig" -> sig) .connectTimeout(2000)
.option(HttpOptions.connTimeout(1000)) .readTimeout(2000)
.option(HttpOptions.readTimeout(5000)) .send("sig="+sig)
.asString .body
val proxies = util.Random.shuffle(list.split('|').toSeq).toSeq val proxies = util.Random.shuffle(list.split('|').toSeq).toSeq
val proxy = proxies(0).split(':') val proxy = proxies(0).split(':')
@@ -65,6 +65,7 @@ object Key {
val proxied = "Proxyed" val proxied = "Proxyed"
val isNAT = "isNAT"
val isRoot = "isRoot" val isRoot = "isRoot"
val status = "status" val status = "status"
val proxyedApps = "proxyedApps" val proxyedApps = "proxyedApps"
@@ -101,7 +102,8 @@ object State {
val INIT = 0 val INIT = 0
val CONNECTING = 1 val CONNECTING = 1
val CONNECTED = 2 val CONNECTED = 2
val STOPPED = 3 val STOPPING = 3
val STOPPED = 4
def isAvailable(state: Int): Boolean = state != CONNECTED && state != CONNECTING def isAvailable(state: Int): Boolean = state != CONNECTED && state != CONNECTING
} }
@@ -69,7 +69,7 @@ object Utils {
val DEFAULT_ROOT: String = "/system/bin/su" val DEFAULT_ROOT: String = "/system/bin/su"
val ALTERNATIVE_ROOT: String = "/system/xbin/su" val ALTERNATIVE_ROOT: String = "/system/xbin/su"
val DEFAULT_IPTABLES: String = "/system/bin/iptables" val DEFAULT_IPTABLES: String = "/system/bin/iptables"
val ALTERNATIVE_IPTABLES: String = "/data/data/com.github.shadowsocks/iptables" val ALTERNATIVE_IPTABLES: String = "iptables"
val TIME_OUT: Int = -99 val TIME_OUT: Int = -99
var initialized: Boolean = false var initialized: Boolean = false
var hasRedirectSupport: Int = -1 var hasRedirectSupport: Int = -1
@@ -181,20 +181,24 @@ object Utils {
} }
def resolve(host: String, addrType: Int): Option[String] = { def resolve(host: String, addrType: Int): Option[String] = {
val lookup = new Lookup(host, addrType) try {
val resolver = new SimpleResolver("114.114.114.114") val lookup = new Lookup(host, addrType)
resolver.setTimeout(5) val resolver = new SimpleResolver("114.114.114.114")
lookup.setResolver(resolver) resolver.setTimeout(5)
val result = lookup.run() lookup.setResolver(resolver)
if (result == null) return None val result = lookup.run()
val records = scala.util.Random.shuffle(result.toList) if (result == null) return None
for (r <- records) { val records = scala.util.Random.shuffle(result.toList)
addrType match { for (r <- records) {
case Type.A => addrType match {
return Some(r.asInstanceOf[ARecord].getAddress.getHostAddress) case Type.A =>
case Type.AAAA => return Some(r.asInstanceOf[ARecord].getAddress.getHostAddress)
return Some(r.asInstanceOf[AAAARecord].getAddress.getHostAddress) case Type.AAAA =>
return Some(r.asInstanceOf[AAAARecord].getAddress.getHostAddress)
}
} }
} catch {
case e: Exception => None
} }
None None
} }
@@ -332,6 +336,16 @@ object Utils {
bitmap bitmap
} }
def getAppIcon(c: Context, packageName: String): Drawable = {
val pm: PackageManager = c.getPackageManager
val icon: Drawable = c.getResources.getDrawable(android.R.drawable.sym_def_app_icon)
try {
pm.getApplicationIcon(packageName)
} catch {
case e: PackageManager.NameNotFoundException => icon
}
}
def getAppIcon(c: Context, uid: Int): Drawable = { def getAppIcon(c: Context, uid: Int): Drawable = {
val pm: PackageManager = c.getPackageManager val pm: PackageManager = c.getPackageManager
val icon: Drawable = c.getResources.getDrawable(android.R.drawable.sym_def_app_icon) val icon: Drawable = c.getResources.getDrawable(android.R.drawable.sym_def_app_icon)