diff --git a/src/main/scala/com/github/shadowsocks/AppManager.scala b/src/main/scala/com/github/shadowsocks/AppManager.scala index eef664cf..ffdb7b71 100644 --- a/src/main/scala/com/github/shadowsocks/AppManager.scala +++ b/src/main/scala/com/github/shadowsocks/AppManager.scala @@ -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 } \ No newline at end of file diff --git a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala index c1b2e44f..2409fb88 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala @@ -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 } \ No newline at end of file diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index c3cdc825..2af8a972 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -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) - } - } - } \ No newline at end of file diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala index a71e3622..dc2c335b 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala @@ -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) } - } } } \ No newline at end of file diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala index 0016692e..5205ea62 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala @@ -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) } \ No newline at end of file diff --git a/src/main/scala/com/github/shadowsocks/Utils.scala b/src/main/scala/com/github/shadowsocks/Utils.scala index ed6fdd7c..3cffe1e4 100644 --- a/src/main/scala/com/github/shadowsocks/Utils.scala +++ b/src/main/scala/com/github/shadowsocks/Utils.scala @@ -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 } } \ No newline at end of file