Commit ee48c077 authored by Mygod's avatar Mygod

Automatic service restart

parent 8fd8514c
......@@ -10,6 +10,5 @@ interface IShadowsocksService {
oneway void registerCallback(IShadowsocksServiceCallback cb);
oneway void unregisterCallback(IShadowsocksServiceCallback cb);
oneway void start(in Config config);
oneway void stop();
oneway void use(in Config config);
}
......@@ -42,10 +42,12 @@ package com.github.shadowsocks
import java.util.{Timer, TimerTask}
import android.app.Service
import android.content.{Intent, Context}
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
import android.os.{Handler, RemoteCallbackList}
import android.util.Log
import android.widget.Toast
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils.{State, TrafficMonitor, TrafficMonitorThread}
import com.github.shadowsocks.utils._
trait BaseService extends Service {
......@@ -57,6 +59,13 @@ trait BaseService extends Service {
final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback]
var callbacksCount: Int = _
lazy val handler = new Handler(getContext.getMainLooper)
private val closeReceiver: BroadcastReceiver = (context: Context, intent: Intent) => {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner()
}
var closeReceiverRegistered: Boolean = _
val binder = new IShadowsocksService.Stub {
override def getMode: Int = {
......@@ -94,17 +103,15 @@ trait BaseService extends Service {
}
}
override def stop() {
if (state == State.CONNECTED) {
stopRunner()
}
}
override def start(config: Config) {
if (state == State.STOPPED) {
startRunner(config)
}
}
override def use(config: Config) = synchronized(state match {
case State.STOPPED => if (config != null) startRunner(config)
case State.CONNECTED =>
if (config == null) stopRunner() else if (config.profileId != BaseService.this.config.profileId) {
stopRunner()
startRunner(config)
}
case _ => Log.w(BaseService.this.getClass.getSimpleName, "Illegal state when invoking use: " + state)
})
}
def startRunner(config: Config) {
......@@ -114,9 +121,24 @@ trait BaseService extends Service {
TrafficMonitor.reset()
trafficMonitorThread = new TrafficMonitorThread(getApplicationContext)
trafficMonitorThread.start()
if (!closeReceiverRegistered) {
// register close receiver
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE)
registerReceiver(closeReceiver, filter)
closeReceiverRegistered = true
}
}
def stopRunner() {
// clean up recevier
if (closeReceiverRegistered) {
unregisterReceiver(closeReceiver)
closeReceiverRegistered = false
}
// Make sure update total traffic when stopping the runner
updateTrafficTotal(TrafficMonitor.txTotal, TrafficMonitor.rxTotal)
......@@ -158,7 +180,6 @@ trait BaseService extends Service {
}
def updateTrafficRate() {
val handler = new Handler(getContext.getMainLooper)
handler.post(() => {
if (callbacksCount > 0) {
val txRate = TrafficMonitor.txRate
......
......@@ -328,7 +328,7 @@ class Shadowsocks
handler.post(() => onActivityResult(Shadowsocks.REQUEST_CONNECT, Activity.RESULT_OK, null))
}
} else {
serviceStart()
serviceLoad()
}
}
}
......@@ -369,21 +369,6 @@ class Shadowsocks
handler.post(() => attachService)
}
def reloadProfile() {
currentProfile = ShadowsocksApplication.currentProfile match {
case Some(profile) => profile // updated
case None => // removed
ShadowsocksApplication.profileManager.getFirstProfile match {
case Some(first) => ShadowsocksApplication.switchProfile(first.id)
case None => ShadowsocksApplication.profileManager.createDefault()
}
}
updatePreferenceScreen()
serviceStop()
}
protected override def onPause() {
super.onPause()
ShadowsocksApplication.profileManager.save
......@@ -434,7 +419,20 @@ class Shadowsocks
ConfigUtils.refresh(this)
// Check if current profile changed
if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile()
if (ShadowsocksApplication.profileId != currentProfile.id) {
currentProfile = ShadowsocksApplication.currentProfile match {
case Some(profile) => profile // updated
case None => // removed
ShadowsocksApplication.profileManager.getFirstProfile match {
case Some(first) => ShadowsocksApplication.switchProfile(first.id)
case None => ShadowsocksApplication.profileManager.createDefault()
}
}
updatePreferenceScreen()
if (serviceStarted) serviceLoad()
}
updateState()
}
......@@ -485,7 +483,7 @@ class Shadowsocks
}
def recovery() {
serviceStop()
if (serviceStarted) serviceStop()
val h = showProgress(R.string.recovering)
ThrowableFuture {
reset()
......@@ -504,14 +502,14 @@ class Shadowsocks
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) = resultCode match {
case Activity.RESULT_OK =>
serviceStart()
serviceLoad()
case _ =>
cancelStart()
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
}
def serviceStop() {
if (bgService != null) bgService.stop()
if (bgService != null) bgService.use(null)
}
def checkText(key: String): Boolean = {
......@@ -522,8 +520,8 @@ class Shadowsocks
}
/** Called when connect button is clicked. */
def serviceStart() {
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
def serviceLoad() {
bgService.use(ConfigUtils.load(ShadowsocksApplication.settings))
if (ShadowsocksApplication.isVpnEnabled) {
changeSwitch(checked = false)
......
......@@ -49,7 +49,6 @@ import android.content.pm.{PackageInfo, PackageManager}
import android.net.{ConnectivityManager, Network}
import android.os._
import android.util.{Log, SparseArray}
import android.widget.Toast
import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._
......@@ -65,7 +64,6 @@ class ShadowsocksNatService extends BaseService {
"-j DNAT --to-destination 127.0.0.1:8123"
private var notification: ShadowsocksNotification = _
var closeReceiver: BroadcastReceiver = _
var connReceiver: BroadcastReceiver = _
val myUid = android.os.Process.myUid()
......@@ -386,16 +384,6 @@ class ShadowsocksNatService extends BaseService {
}
super.startRunner(config)
// register close receiver
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE)
closeReceiver = (context: Context, intent: Intent) => {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner()
}
registerReceiver(closeReceiver, filter)
ShadowsocksApplication.track(TAG, "start")
changeState(State.CONNECTING)
......@@ -450,12 +438,6 @@ class ShadowsocksNatService extends BaseService {
// channge the state
changeState(State.STOPPING)
// clean up recevier
if (closeReceiver != null) {
unregisterReceiver(closeReceiver)
closeReceiver = null
}
if (notification != null) notification.destroy()
ShadowsocksApplication.track(TAG, "stop")
......
package com.github.shadowsocks
import android.content.res.Resources
import android.os.{Handler, Build, Bundle}
import android.os.{Build, Bundle}
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.{Toolbar, DefaultItemAnimator, LinearLayoutManager, RecyclerView}
import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar}
import android.view.{LayoutInflater, View, ViewGroup}
import android.widget.CheckedTextView
import com.github.shadowsocks.database.Profile
......@@ -31,12 +31,8 @@ class ShadowsocksQuickSwitchActivity extends AppCompatActivity {
}
def onClick(v: View) {
Utils.stopSsService(ShadowsocksQuickSwitchActivity.this)
ShadowsocksApplication.switchProfile(item.id)
val handler = new Handler(ShadowsocksQuickSwitchActivity.this.getMainLooper)
handler.postDelayed(() => {
Utils.startSsService(ShadowsocksQuickSwitchActivity.this)
}, 3000)
Utils.startSsService(ShadowsocksQuickSwitchActivity.this)
finish
}
}
......
......@@ -65,7 +65,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext {
onActivityResult(Shadowsocks.REQUEST_CONNECT, Activity.RESULT_OK, null)
}
} else {
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
bgService.use(ConfigUtils.load(ShadowsocksApplication.settings))
finish()
}
}
......@@ -101,7 +101,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext {
resultCode match {
case Activity.RESULT_OK =>
if (bgService != null) {
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
bgService.use(ConfigUtils.load(ShadowsocksApplication.settings))
}
case _ =>
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
......
......@@ -61,11 +61,11 @@ class ShadowsocksRunnerService extends Service with ServiceBoundContext {
val intent = VpnService.prepare(ShadowsocksRunnerService.this)
if (intent == null) {
if (bgService != null) {
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
bgService.use(ConfigUtils.load(ShadowsocksApplication.settings))
}
}
} else {
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
bgService.use(ConfigUtils.load(ShadowsocksApplication.settings))
}
stopSelf()
}
......
......@@ -49,7 +49,6 @@ import android.content.pm.{PackageInfo, PackageManager}
import android.net.VpnService
import android.os._
import android.util.Log
import android.widget.Toast
import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._
import org.apache.commons.net.util.SubnetUtils
......@@ -64,7 +63,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
var conn: ParcelFileDescriptor = _
var vpnThread: ShadowsocksVpnThread = _
private var notification: ShadowsocksNotification = _
var closeReceiver: BroadcastReceiver = _
var sslocalProcess: Process = _
var sstunnelProcess: Process = _
......@@ -134,12 +132,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
conn = null
}
// clean up recevier
if (closeReceiver != null) {
unregisterReceiver(closeReceiver)
closeReceiver = null
}
super.stopRunner()
}
......@@ -181,16 +173,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
vpnThread = new ShadowsocksVpnThread(this)
vpnThread.start()
// register close receiver
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE)
closeReceiver = (context: Context, intent: Intent) => {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner()
}
registerReceiver(closeReceiver, filter)
// ensure the VPNService is prepared
if (VpnService.prepare(this) != null) {
val i = new Intent(this, classOf[ShadowsocksRunnerActivity])
......
......@@ -66,7 +66,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
case _: Exception => // ignore
}
serverSocket = null
}
}
}
def stopThread() {
......
......@@ -39,7 +39,6 @@
package com.github.shadowsocks
import android.content.{BroadcastReceiver, Context, Intent}
import android.os.Handler
import com.github.shadowsocks.helper.TaskerSettings
import com.github.shadowsocks.utils.Utils
......@@ -51,15 +50,10 @@ class TaskerReceiver extends BroadcastReceiver {
val settings = TaskerSettings.fromIntent(intent)
val switched = ShadowsocksApplication.profileManager.getProfile(settings.profileId) match {
case Some(p) =>
Utils.stopSsService(context)
ShadowsocksApplication.switchProfile(settings.profileId)
true
case _ => false
}
val handler = new Handler(context.getMainLooper)
handler.postDelayed(() => {
if (settings.switchOn) Utils.startSsService(context)
else if (!switched) Utils.stopSsService(context)
}, 3000)
if (settings.switchOn) Utils.startSsService(context) else Utils.stopSsService(context)
}
}
......@@ -72,8 +72,8 @@ object Key {
val isProxyApps = "isProxyApps"
val isBypassApps = "isBypassApps"
val isUdpDns = "isUdpDns"
val isAuth= "isAuth"
val isIpv6= "isIpv6"
val isAuth = "isAuth"
val isIpv6 = "isIpv6"
val proxy = "proxy"
val sitekey = "sitekey"
......
package com.github.shadowsocks.utils
import java.lang.System
import java.text.DecimalFormat
import com.github.shadowsocks.{R, ShadowsocksApplication}
......@@ -81,4 +80,3 @@ object TrafficMonitor {
dirty = true
}
}
......@@ -42,7 +42,7 @@ import java.io._
import java.net._
import java.security.MessageDigest
import android.animation.{AnimatorListenerAdapter, Animator}
import android.animation.{Animator, AnimatorListenerAdapter}
import android.app.ActivityManager
import android.content.pm.{ApplicationInfo, PackageManager}
import android.content.{Context, Intent}
......@@ -51,11 +51,11 @@ import android.graphics.drawable.{BitmapDrawable, Drawable}
import android.os.Build
import android.provider.Settings
import android.support.v4.content.ContextCompat
import android.util.{DisplayMetrics, Base64, Log}
import android.util.{Base64, DisplayMetrics, Log}
import android.view.View.MeasureSpec
import android.view.{Gravity, View, Window}
import android.widget.Toast
import com.github.shadowsocks.{ShadowsocksRunnerService, ShadowsocksApplication, BuildConfig}
import com.github.shadowsocks.{BuildConfig, ShadowsocksApplication, ShadowsocksRunnerService}
import org.xbill.DNS._
......@@ -401,7 +401,7 @@ object Utils {
}
}
def startSsService(context: Context): Unit = {
def startSsService(context: Context) {
val isInstalled: Boolean = ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)
if (!isInstalled) return
......@@ -409,8 +409,9 @@ object Utils {
context.startService(intent)
}
def stopSsService(context: Context): Unit = {
context.sendBroadcast(new Intent(Action.CLOSE))
def stopSsService(context: Context) {
val intent = new Intent(Action.CLOSE)
context.sendBroadcast(intent)
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment