diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 513a1396..54727274 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -54,7 +54,7 @@ diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index 7993f8db..c2099578 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -531,7 +531,6 @@ class Shadowsocks onContentChanged() } - /** Called when the activity is first created. */ override def onCreate(savedInstanceState: Bundle) { // Initialize the preference @@ -580,17 +579,22 @@ class Shadowsocks // Register broadcast receiver registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS)) - // Bind to the service - spawn { - status.edit.putBoolean(Key.isRoot, Utils.getRoot).apply() - handler.post(new Runnable { - override def run() { - val isRoot = status.getBoolean(Key.isRoot, false) - val s = if (isRoot) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService] - bindService(new Intent(s.getName), connection, Context.BIND_AUTO_CREATE) - startService(new Intent(Shadowsocks.this, s)) - } - }) + } + + def attachService() { + if (bgService == null) { + val isRoot = status.getBoolean(Key.isRoot, false) + val s = if (isRoot) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService] + bindService(new Intent(s.getName), connection, Context.BIND_AUTO_CREATE) + startService(new Intent(Shadowsocks.this, s)) + } + } + + def deattachService() { + if (bgService != null) { + bgService.unregisterCallback(callback) + unbindService(connection) + bgService = null } } @@ -806,11 +810,22 @@ class Shadowsocks protected override def onPause() { super.onPause() + deattachService() prepared = false } protected override def onResume() { super.onResume() + // Bind to the service + spawn { + val isRoot = Utils.getRoot + handler.post(new Runnable { + override def run() { + status.edit.putBoolean(Key.isRoot, isRoot).commit() + attachService() + } + }) + } ConfigUtils.refresh(this) } @@ -860,10 +875,6 @@ class Shadowsocks override def onDestroy() { super.onDestroy() - if (bgService != null) { - bgService.unregisterCallback(callback) - unbindService(connection) - } Crouton.cancelAllCroutons() unregisterReceiver(preferenceReceiver) new BackupManager(this).dataChanged() @@ -913,6 +924,7 @@ class Shadowsocks } case _ => clearDialog() + switchButton.setChecked(false) Log.e(Shadowsocks.TAG, "Failed to start VpnService") } } @@ -921,7 +933,7 @@ class Shadowsocks def isVpnEnabled: Boolean = { if (Shadowsocks.vpnEnabled < 0) { Shadowsocks.vpnEnabled = if (Build.VERSION.SDK_INT - >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !Utils.getRoot) { + >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !status.getBoolean(Key.isRoot, false)) { 1 } else { 0 diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index 7a826ebf..445e2141 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -57,6 +57,7 @@ import org.apache.http.conn.util.InetAddressUtils import scala.collection._ import java.util.{TimerTask, Timer} import android.net.TrafficStats +import scala.concurrent.ops._ import com.github.shadowsocks.utils._ import scala.Some import android.graphics.Color @@ -208,7 +209,7 @@ class ShadowsocksNatService extends Service with BaseService { if (rate >= 0) { val bitmap = Utils .getBitmap(rate.toString, icon.getIntrinsicWidth * 4, icon.getIntrinsicHeight * 4, - Color.TRANSPARENT) + Color.TRANSPARENT) builder.setLargeIcon(bitmap) if (rate < 1000) { @@ -421,7 +422,7 @@ class ShadowsocksNatService extends Service with BaseService { override def startRunner(c: Config) { - var config = c + config = c // register close receiver val filter = new IntentFilter() @@ -474,46 +475,48 @@ class ShadowsocksNatService extends Service with BaseService { timer.schedule(task, TIMER_INTERVAL * 1000, TIMER_INTERVAL * 1000) } - if (config.proxy == "198.199.101.152") { - val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer - try { - config = ConfigUtils.getPublicConfig(getBaseContext, container, config) - } catch { - case ex: Exception => - notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) - stopRunner() - handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500) - return + spawn { + if (config.proxy == "198.199.101.152") { + val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer + try { + config = ConfigUtils.getPublicConfig(getBaseContext, container, config) + } catch { + case ex: Exception => + notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) + stopRunner() + handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500) + return + } } - } - killProcesses() + killProcesses() - var resolved: Boolean = false - if (!InetAddressUtils.isIPv4Address(config.proxy) && - !InetAddressUtils.isIPv6Address(config.proxy)) { - Utils.resolve(config.proxy, enableIPv6 = true) match { - case Some(a) => - config.proxy = a - resolved = true - case None => resolved = false + var resolved: Boolean = false + if (!InetAddressUtils.isIPv4Address(config.proxy) && + !InetAddressUtils.isIPv6Address(config.proxy)) { + Utils.resolve(config.proxy, enableIPv6 = true) match { + case Some(a) => + config.proxy = a + resolved = true + case None => resolved = false + } + } else { + resolved = true } - } else { - resolved = true - } - hasRedirectSupport = Utils.getHasRedirectSupport + hasRedirectSupport = Utils.getHasRedirectSupport - if (resolved && handleConnection) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).format(config.profileName)) - handler.sendEmptyMessageDelayed(Msg.CONNECT_SUCCESS, 500) - } else { - notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) - stopRunner() - handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500) + if (resolved && handleConnection) { + notifyForegroundAlert(getString(R.string.forward_success), + getString(R.string.service_running).format(config.profileName)) + handler.sendEmptyMessageDelayed(Msg.CONNECT_SUCCESS, 500) + } else { + notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) + stopRunner() + handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500) + } + handler.sendEmptyMessageDelayed(Msg.CONNECT_FINISH, 500) } - handler.sendEmptyMessageDelayed(Msg.CONNECT_FINISH, 500) } override def stopRunner() { diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala index 841c406b..72ecaef1 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala @@ -64,8 +64,7 @@ class ShadowsocksReceiver extends BroadcastReceiver { val isAutoConnect: Boolean = settings.getBoolean(Key.isAutoConnect, false) val isInstalled: Boolean = status.getBoolean(versionName, false) if (isAutoConnect && isInstalled) { - if (Utils.getRoot) { - } + context.startActivity(new Intent(context, classOf[ShadowsocksRunnerActivity])) } } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowVpnActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala similarity index 70% rename from src/main/scala/com/github/shadowsocks/ShadowVpnActivity.scala rename to src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala index a2e9940a..4e929c2d 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowVpnActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala @@ -42,41 +42,68 @@ package com.github.shadowsocks import android.app.Activity import android.os.{IBinder, Bundle} import android.net.VpnService -import android.content.{ComponentName, ServiceConnection, Intent} +import android.content.{Context, ComponentName, ServiceConnection, Intent} import android.util.Log import android.preference.PreferenceManager import com.github.shadowsocks.utils._ import com.github.shadowsocks.aidl.IShadowsocksService -class ShadowVpnActivity extends Activity { +class ShadowsocksRunnerActivity extends Activity { + + lazy val settings = PreferenceManager.getDefaultSharedPreferences(this) + lazy val isRoot = Utils.getRoot // Services var bgService: IShadowsocksService = null val connection = new ServiceConnection { override def onServiceConnected(name: ComponentName, service: IBinder) { bgService = IShadowsocksService.Stub.asInterface(service) + if (!isRoot) { + val intent = VpnService.prepare(ShadowsocksRunnerActivity.this) + if (intent != null) { + startActivityForResult(intent, Shadowsocks.REQUEST_CONNECT) + } else { + onActivityResult(Shadowsocks.REQUEST_CONNECT, Activity.RESULT_OK, null) + } + } else { + bgService.start(ConfigUtils.load(settings)) + } } - override def onServiceDisconnected(name: ComponentName) { bgService = null } } + def attachService() { + if (bgService == null) { + val s = if (isRoot) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService] + bindService(new Intent(s.getName), connection, Context.BIND_AUTO_CREATE) + startService(new Intent(this, s)) + } + } + + def deattachService() { + if (bgService != null) { + unbindService(connection) + bgService = null + } + } + override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) - val intent = VpnService.prepare(this) - if (intent != null) { - startActivityForResult(intent, Shadowsocks.REQUEST_CONNECT) - } else { - onActivityResult(Shadowsocks.REQUEST_CONNECT, Activity.RESULT_OK, null) - } + attachService() + } + + override def onDestroy() { + super.onDestroy() + deattachService() } override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { resultCode match { case Activity.RESULT_OK => if (bgService != null) { - bgService.start(ConfigUtils.load(PreferenceManager.getDefaultSharedPreferences(this))) + bgService.start(ConfigUtils.load(settings)) } case _ => Log.e(Shadowsocks.TAG, "Failed to start VpnService") diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 89271595..50edf476 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -51,11 +51,12 @@ import java.io._ import android.net.VpnService import org.apache.http.conn.util.InetAddressUtils import android.os.Message +import scala.concurrent.ops._ import org.apache.commons.net.util.SubnetUtils import java.net.InetAddress import com.github.shadowsocks.utils._ import scala.Some -import com.github.shadowsocks.aidl.Config +import com.github.shadowsocks.aidl.{IShadowsocksService, Config} class ShadowsocksVpnService extends VpnService with BaseService { @@ -266,6 +267,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) .asInstanceOf[NotificationManager] + } def killProcesses() { @@ -291,11 +293,11 @@ class ShadowsocksVpnService extends VpnService with BaseService { override def startRunner(c: Config) { - var config = c + config = c // ensure the VPNService is prepared if (VpnService.prepare(this) != null) { - val i = new Intent(this, classOf[ShadowVpnActivity]) + val i = new Intent(this, classOf[ShadowsocksRunnerActivity]) i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(i) return @@ -321,44 +323,46 @@ class ShadowsocksVpnService extends VpnService with BaseService { changeState(State.CONNECTING) - if (config.proxy == "198.199.101.152") { - val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer - try { - config = ConfigUtils.getPublicConfig(getBaseContext, container, config) - } catch { - case ex: Exception => - notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) - stopRunner() - handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500) - return + spawn { + if (config.proxy == "198.199.101.152") { + val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer + try { + config = ConfigUtils.getPublicConfig(getBaseContext, container, config) + } catch { + case ex: Exception => + notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) + stopRunner() + handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500) + return + } } - } - // reset the context - killProcesses() + // reset the context + killProcesses() - // Resolve the server address - var resolved: Boolean = false - if (!InetAddressUtils.isIPv4Address(config.proxy) && - !InetAddressUtils.isIPv6Address(config.proxy)) { - Utils.resolve(config.proxy, enableIPv6 = true) match { - case Some(addr) => - config.proxy = addr - resolved = true - case None => resolved = false + // Resolve the server address + var resolved: Boolean = false + if (!InetAddressUtils.isIPv4Address(config.proxy) && + !InetAddressUtils.isIPv6Address(config.proxy)) { + Utils.resolve(config.proxy, enableIPv6 = true) match { + case Some(addr) => + config.proxy = addr + resolved = true + case None => resolved = false + } + } else { + resolved = true } - } else { - resolved = true - } - if (resolved && handleConnection) { - handler.sendEmptyMessageDelayed(Msg.CONNECT_SUCCESS, 300) - } else { - notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) - handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 300) - stopRunner() + if (resolved && handleConnection) { + handler.sendEmptyMessageDelayed(Msg.CONNECT_SUCCESS, 300) + } else { + notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) + handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 300) + stopRunner() + } + handler.sendEmptyMessageDelayed(Msg.CONNECT_FINISH, 300) } - handler.sendEmptyMessageDelayed(Msg.CONNECT_FINISH, 300) } override def stopRunner() { @@ -394,6 +398,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { } override def getTag = TAG - override def getServiceMode = Mode.VPN + }