From ee48c0773c2d7f90f9ad2802893427886974696c Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 12 Mar 2016 13:36:04 +0800 Subject: [PATCH] Automatic service restart --- .../shadowsocks/aidl/IShadowsocksService.aidl | 3 +- .../com/github/shadowsocks/BaseService.scala | 49 +++++++++++++------ .../com/github/shadowsocks/Shadowsocks.scala | 42 ++++++++-------- .../shadowsocks/ShadowsocksNatService.scala | 18 ------- .../ShadowsocksQuickSwitchActivity.scala | 10 ++-- .../ShadowsocksRunnerActivity.scala | 4 +- .../ShadowsocksRunnerService.scala | 4 +- .../shadowsocks/ShadowsocksVpnService.scala | 18 ------- .../shadowsocks/ShadowsocksVpnThread.scala | 2 +- .../github/shadowsocks/TaskerReceiver.scala | 8 +-- .../github/shadowsocks/utils/Constants.scala | 4 +- .../shadowsocks/utils/TrafficMonitor.scala | 2 - .../com/github/shadowsocks/utils/Utils.scala | 13 ++--- 13 files changed, 74 insertions(+), 103 deletions(-) diff --git a/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl b/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl index 70667310..4a45e8cd 100644 --- a/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl +++ b/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl @@ -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); } diff --git a/src/main/scala/com/github/shadowsocks/BaseService.scala b/src/main/scala/com/github/shadowsocks/BaseService.scala index 346e5485..0832b430 100644 --- a/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -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 diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index c854631d..d89efd52 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -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) diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index e16e7e00..15df87ac 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -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") diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksQuickSwitchActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksQuickSwitchActivity.scala index 4467428a..962f3a9f 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksQuickSwitchActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksQuickSwitchActivity.scala @@ -1,9 +1,9 @@ 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 } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala index 81c5f57d..869a5ad0 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala @@ -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") diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala index e37ec801..721d46a8 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala @@ -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() } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 8f8d03b3..89d15cdc 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -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]) diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnThread.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnThread.scala index fcb99498..d1361327 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnThread.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnThread.scala @@ -66,7 +66,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread { case _: Exception => // ignore } serverSocket = null - } + } } def stopThread() { diff --git a/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala b/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala index 01103b17..1a3ed99d 100644 --- a/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala +++ b/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala @@ -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) } } diff --git a/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/src/main/scala/com/github/shadowsocks/utils/Constants.scala index ee4bccd4..08f9f77d 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Constants.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Constants.scala @@ -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" diff --git a/src/main/scala/com/github/shadowsocks/utils/TrafficMonitor.scala b/src/main/scala/com/github/shadowsocks/utils/TrafficMonitor.scala index 8ffee8df..f84015b6 100644 --- a/src/main/scala/com/github/shadowsocks/utils/TrafficMonitor.scala +++ b/src/main/scala/com/github/shadowsocks/utils/TrafficMonitor.scala @@ -1,6 +1,5 @@ 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 } } - diff --git a/src/main/scala/com/github/shadowsocks/utils/Utils.scala b/src/main/scala/com/github/shadowsocks/utils/Utils.scala index 860e9821..fa5e582a 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Utils.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Utils.scala @@ -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) } }