Commit 143afb05 authored by Mygod's avatar Mygod

Use startService calls instead of AIDL to start services

Because our data store is process-safe, we can remove the `use` function
entirely and move more components to :bg process.
parent 27fc2734
...@@ -96,18 +96,14 @@ ...@@ -96,18 +96,14 @@
android:label="@string/quick_toggle" android:label="@string/quick_toggle"
android:theme="@android:style/Theme.Translucent.NoTitleBar" android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:taskAffinity="" android:taskAffinity=""
android:process=":bg"
android:launchMode="singleTask"> android:launchMode="singleTask">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" /> <action android:name="android.intent.action.CREATE_SHORTCUT" />
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name=".ShadowsocksRunnerService"
android:exported="false">
</service>
<service <service
android:name=".ShadowsocksNatService" android:name=".ShadowsocksNatService"
android:process=":bg" android:process=":bg"
...@@ -134,13 +130,13 @@ ...@@ -134,13 +130,13 @@
</intent-filter> </intent-filter>
</service> </service>
<receiver android:name=".BootReceiver" android:enabled="false"> <receiver android:name=".BootReceiver" android:process=":bg" android:enabled="false">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".TaskerReceiver" tools:ignore="ExportedReceiver"> <receiver android:name=".TaskerReceiver" android:process=":bg" tools:ignore="ExportedReceiver">
<intent-filter> <intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/> <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>
</intent-filter> </intent-filter>
......
...@@ -10,7 +10,4 @@ interface IShadowsocksService { ...@@ -10,7 +10,4 @@ interface IShadowsocksService {
oneway void startListeningForBandwidth(IShadowsocksServiceCallback cb); oneway void startListeningForBandwidth(IShadowsocksServiceCallback cb);
oneway void stopListeningForBandwidth(IShadowsocksServiceCallback cb); oneway void stopListeningForBandwidth(IShadowsocksServiceCallback cb);
oneway void unregisterCallback(IShadowsocksServiceCallback cb); oneway void unregisterCallback(IShadowsocksServiceCallback cb);
oneway void use(in int profileId);
void useSync(in int profileId);
} }
...@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit ...@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit
import java.util.{Timer, TimerTask} import java.util.{Timer, TimerTask}
import android.app.Service import android.app.Service
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter} import android.content._
import android.os.{Build, Handler, IBinder, RemoteCallbackList} import android.os.{Build, Handler, IBinder, RemoteCallbackList}
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
...@@ -65,9 +65,11 @@ trait BaseService extends Service { ...@@ -65,9 +65,11 @@ trait BaseService extends Service {
lazy val restartHanlder = new Handler(getMainLooper) lazy val restartHanlder = new Handler(getMainLooper)
private var notification: ShadowsocksNotification = _ private var notification: ShadowsocksNotification = _
private val closeReceiver: BroadcastReceiver = (context: Context, _: Intent) => { private val closeReceiver: BroadcastReceiver = (context: Context, intent: Intent) => intent.getAction match {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show() case Action.RELOAD => forceLoad()
stopRunner(stopService = true) case _ =>
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner(stopService = true)
} }
var closeReceiverRegistered: Boolean = _ var closeReceiverRegistered: Boolean = _
...@@ -103,20 +105,6 @@ trait BaseService extends Service { ...@@ -103,20 +105,6 @@ trait BaseService extends Service {
stopListeningForBandwidth(cb) // saves an RPC, and safer stopListeningForBandwidth(cb) // saves an RPC, and safer
callbacks.unregister(cb) callbacks.unregister(cb)
} }
override def use(profileId: Int): Unit = synchronized(if (profileId < 0) stopRunner(stopService = true) else {
val profile = app.profileManager.getProfile(profileId).orNull
if (profile == null) stopRunner(stopService = true, getString(R.string.profile_empty)) else state match {
case State.STOPPED => if (checkProfile(profile)) startRunner(profile)
case State.CONNECTED => if (profileId != BaseService.this.profile.id && checkProfile(profile)) {
stopRunner(stopService = false)
startRunner(profile)
}
case _ => Log.w(BaseService.this.getClass.getSimpleName, "Illegal state when invoking use: " + state)
}
})
override def useSync(profileId: Int): Unit = use(profileId)
} }
def checkProfile(profile: Profile): Boolean = if (TextUtils.isEmpty(profile.host) || TextUtils.isEmpty(profile.password)) { def checkProfile(profile: Profile): Boolean = if (TextUtils.isEmpty(profile.host) || TextUtils.isEmpty(profile.password)) {
...@@ -124,6 +112,17 @@ trait BaseService extends Service { ...@@ -124,6 +112,17 @@ trait BaseService extends Service {
false false
} else true } else true
def forceLoad(): Unit = app.currentProfile.orNull match {
case null => stopRunner(stopService = true, getString(R.string.profile_empty))
case p => if (checkProfile(p)) state match {
case State.STOPPED => startRunner()
case State.CONNECTED =>
stopRunner(stopService = false)
startRunner()
case s => Log.w(BaseService.this.getClass.getSimpleName, "Illegal state when invoking use: " + s)
}
}
def connect() { def connect() {
profile.name = profile.getName // save original name before it's (possibly) overwritten by IP addresses profile.name = profile.getName // save original name before it's (possibly) overwritten by IP addresses
...@@ -161,38 +160,8 @@ trait BaseService extends Service { ...@@ -161,38 +160,8 @@ trait BaseService extends Service {
} }
def createNotification(): ShadowsocksNotification def createNotification(): ShadowsocksNotification
def startRunner(profile: Profile) { def startRunner(): Unit = if (Build.VERSION.SDK_INT >= 26) startForegroundService(new Intent(this, getClass))
this.profile = profile
if (Build.VERSION.SDK_INT >= 26) startForegroundService(new Intent(this, getClass))
else startService(new Intent(this, getClass)) else startService(new Intent(this, getClass))
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
}
notification = createNotification()
app.track(getClass.getSimpleName, "start")
changeState(State.CONNECTING)
Utils.ThrowableFuture(try connect() catch {
case _: NameNotResolvedException => stopRunner(stopService = true, getString(R.string.invalid_server))
case _: NullConnectionException => stopRunner(stopService = true, getString(R.string.reboot_required))
case exc: Throwable =>
stopRunner(stopService = true, getString(R.string.service_failed) + ": " + exc.getMessage)
exc.printStackTrace()
app.track(exc)
})
}
def stopRunner(stopService: Boolean, msg: String = null) { def stopRunner(stopService: Boolean, msg: String = null) {
// clean up recevier // clean up recevier
...@@ -281,7 +250,41 @@ trait BaseService extends Service { ...@@ -281,7 +250,41 @@ trait BaseService extends Service {
} }
// Service of shadowsocks should always be started explicitly // Service of shadowsocks should always be started explicitly
override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = Service.START_NOT_STICKY override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = {
state match {
case State.STOPPED | State.IDLE =>
case _ => return Service.START_NOT_STICKY // ignore request
}
profile = app.currentProfile.orNull
TrafficMonitor.reset()
trafficMonitorThread = new TrafficMonitorThread(getApplicationContext)
trafficMonitorThread.start()
if (!closeReceiverRegistered) {
// register close receiver
val filter = new IntentFilter()
filter.addAction(Action.RELOAD)
filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE)
registerReceiver(closeReceiver, filter)
closeReceiverRegistered = true
}
notification = createNotification()
app.track(getClass.getSimpleName, "start")
changeState(State.CONNECTING)
Utils.ThrowableFuture(try connect() catch {
case _: NameNotResolvedException => stopRunner(stopService = true, getString(R.string.invalid_server))
case _: NullConnectionException => stopRunner(stopService = true, getString(R.string.reboot_required))
case exc: Throwable =>
stopRunner(stopService = true, getString(R.string.service_failed) + ": " + exc.getMessage)
exc.printStackTrace()
app.track(exc)
})
Service.START_NOT_STICKY
}
protected def changeState(s: Int, msg: String = null) { protected def changeState(s: Int, msg: String = null) {
val handler = new Handler(getMainLooper) val handler = new Handler(getMainLooper)
......
...@@ -175,7 +175,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe ...@@ -175,7 +175,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
}) })
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = resultCode match { override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = resultCode match {
case Activity.RESULT_OK => bgService.use(app.dataStore.profileId) case Activity.RESULT_OK => Utils.startSsService(this)
case _ => Log.e(TAG, "Failed to start VpnService") case _ => Log.e(TAG, "Failed to start VpnService")
} }
...@@ -299,8 +299,8 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe ...@@ -299,8 +299,8 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
fab = findViewById(R.id.fab).asInstanceOf[FloatingActionButton] fab = findViewById(R.id.fab).asInstanceOf[FloatingActionButton]
fabProgressCircle = findViewById(R.id.fabProgressCircle).asInstanceOf[FABProgressCircle] fabProgressCircle = findViewById(R.id.fabProgressCircle).asInstanceOf[FABProgressCircle]
fab.setOnClickListener(_ => if (state == State.CONNECTED) bgService.use(-1) else Utils.ThrowableFuture { fab.setOnClickListener(_ => if (state == State.CONNECTED) Utils.stopSsService(this) else Utils.ThrowableFuture {
if (app.isNatEnabled) bgService.use(app.dataStore.profileId) else { if (app.isNatEnabled) Utils.startSsService(this) else {
val intent = VpnService.prepare(this) val intent = VpnService.prepare(this)
if (intent != null) startActivityForResult(intent, REQUEST_CONNECT) if (intent != null) startActivityForResult(intent, REQUEST_CONNECT)
else handler.post(() => onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null)) else handler.post(() => onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null))
...@@ -369,7 +369,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe ...@@ -369,7 +369,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
case DRAWER_PROFILES => displayFragment(new ProfilesFragment) case DRAWER_PROFILES => displayFragment(new ProfilesFragment)
case DRAWER_RECOVERY => case DRAWER_RECOVERY =>
app.track("GlobalConfigFragment", "reset") app.track("GlobalConfigFragment", "reset")
if (bgService != null) bgService.use(-1) Utils.stopSsService(this)
val dialog = ProgressDialog.show(this, "", getString(R.string.recovering), true, false) val dialog = ProgressDialog.show(this, "", getString(R.string.recovering), true, false)
val handler = new Handler { val handler = new Handler {
override def handleMessage(msg: Message): Unit = if (dialog.isShowing && !isDestroyed) dialog.dismiss() override def handleMessage(msg: Message): Unit = if (dialog.isShowing && !isDestroyed) dialog.dismiss()
......
...@@ -170,7 +170,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic ...@@ -170,7 +170,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
app.switchProfile(item.id) app.switchProfile(item.id)
profilesAdapter.refreshId(old) profilesAdapter.refreshId(old)
itemView.setSelected(true) itemView.setSelected(true)
if (activity.state == State.CONNECTED) activity.bgService.use(item.id) // reconnect to new profile if (activity.state == State.CONNECTED) Utils.reloadSsService(activity)
} }
override def onMenuItemClick(menu: MenuItem): Boolean = menu.getItemId match { override def onMenuItemClick(menu: MenuItem): Boolean = menu.getItemId match {
......
...@@ -24,11 +24,12 @@ import java.io.File ...@@ -24,11 +24,12 @@ import java.io.File
import java.net.{Inet6Address, InetAddress} import java.net.{Inet6Address, InetAddress}
import java.util.Locale import java.util.Locale
import android.app.Service
import android.content._ import android.content._
import android.os._ import android.os._
import android.util.Log import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.acl.{AclSyncJob, Acl} import com.github.shadowsocks.acl.{Acl, AclSyncJob}
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import eu.chainfire.libsuperuser.Shell import eu.chainfire.libsuperuser.Shell
...@@ -176,15 +177,17 @@ class ShadowsocksNatService extends BaseService { ...@@ -176,15 +177,17 @@ class ShadowsocksNatService extends BaseService {
su.addCommand((init_sb ++ http_sb).toArray) su.addCommand((init_sb ++ http_sb).toArray)
} }
override def startRunner(profile: Profile): Unit = if (su == null) override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = if (su == null) {
su = new Shell.Builder().useSU().setWantSTDERR(true).setWatchdogTimeout(10).open((_, exitCode, _) => su = new Shell.Builder().useSU().setWantSTDERR(true).setWatchdogTimeout(10).open((_, exitCode, _) =>
if (exitCode == 0) super.startRunner(profile) else { if (exitCode == 0) super.onStartCommand(intent, flags, startId) else {
if (su != null) { if (su != null) {
su.close() su.close()
su = null su = null
} }
super.stopRunner(stopService = true, getString(R.string.nat_no_root)) super.stopRunner(stopService = true, getString(R.string.nat_no_root))
}) })
Service.START_NOT_STICKY
} else super.onStartCommand(intent, flags, startId)
override def connect() { override def connect() {
super.connect() super.connect()
......
...@@ -26,6 +26,7 @@ import android.net.VpnService ...@@ -26,6 +26,7 @@ import android.net.VpnService
import android.os.{Bundle, Handler} import android.os.{Bundle, Handler}
import android.util.Log import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.utils.Utils
object ShadowsocksRunnerActivity { object ShadowsocksRunnerActivity {
private final val TAG = "ShadowsocksRunnerActivity" private final val TAG = "ShadowsocksRunnerActivity"
...@@ -46,7 +47,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext { ...@@ -46,7 +47,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext {
def startBackgroundService() { def startBackgroundService() {
if (app.isNatEnabled) { if (app.isNatEnabled) {
bgService.use(app.dataStore.profileId) Utils.startSsService(this)
finish() finish()
} else { } else {
val intent = VpnService.prepare(ShadowsocksRunnerActivity.this) val intent = VpnService.prepare(ShadowsocksRunnerActivity.this)
...@@ -87,10 +88,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext { ...@@ -87,10 +88,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext {
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
resultCode match { resultCode match {
case Activity.RESULT_OK => case Activity.RESULT_OK => Utils.startSsService(this)
if (bgService != null) {
bgService.use(app.dataStore.profileId)
}
case _ => case _ =>
Log.e(TAG, "Failed to start VpnService") Log.e(TAG, "Failed to start VpnService")
} }
......
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package com.github.shadowsocks
import android.app.Service
import android.content.Intent
import android.net.VpnService
import android.os.{Build, Handler, IBinder}
import android.support.v4.app.NotificationCompat
import android.support.v4.os.BuildCompat
import com.github.shadowsocks.ShadowsocksApplication.app
class ShadowsocksRunnerService extends Service with ServiceBoundContext {
val handler = new Handler()
override def onBind(intent: Intent): IBinder = {
null
}
override def onServiceConnected() {
handler.postDelayed(() => {
if (bgService != null) {
if (app.isNatEnabled) startBackgroundService()
else if (VpnService.prepare(ShadowsocksRunnerService.this) == null) startBackgroundService()
handler.postDelayed(() => stopSelf(), 3000)
}
}, 1000)
}
def startBackgroundService(): Unit = bgService.useSync(app.dataStore.profileId)
override def onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= 26) {
val builder = new NotificationCompat.Builder(this)
builder.setPriority(NotificationCompat.PRIORITY_MIN)
startForeground(1, builder.build)
}
attachService()
}
override def onDestroy() {
super.onDestroy()
stopForeground(true)
detachService()
}
}
...@@ -23,6 +23,7 @@ package com.github.shadowsocks ...@@ -23,6 +23,7 @@ package com.github.shadowsocks
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
import android.app.Service
import android.content._ import android.content._
import android.content.pm.PackageManager.NameNotFoundException import android.content.pm.PackageManager.NameNotFoundException
import android.net.VpnService import android.net.VpnService
...@@ -30,7 +31,6 @@ import android.os._ ...@@ -30,7 +31,6 @@ import android.os._
import android.util.Log import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.acl.{Acl, AclSyncJob, Subnet} import com.github.shadowsocks.acl.{Acl, AclSyncJob, Subnet}
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
...@@ -100,18 +100,15 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -100,18 +100,15 @@ class ShadowsocksVpnService extends VpnService with BaseService {
} }
} }
override def startRunner(profile: Profile) { override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = {
// ensure the VPNService is prepared // ensure the VPNService is prepared
if (VpnService.prepare(this) != null) { if (VpnService.prepare(this) != null) {
val i = new Intent(this, classOf[ShadowsocksRunnerActivity]) val i = new Intent(this, classOf[ShadowsocksRunnerActivity])
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(i) startActivity(i)
stopRunner(stopService = true) stopRunner(stopService = true)
return Service.START_NOT_STICKY
} } else super.onStartCommand(intent, flags, startId)
super.startRunner(profile)
} }
override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-vpn") override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-vpn")
......
...@@ -30,10 +30,17 @@ import com.github.shadowsocks.ShadowsocksApplication.app ...@@ -30,10 +30,17 @@ import com.github.shadowsocks.ShadowsocksApplication.app
class TaskerReceiver extends BroadcastReceiver { class TaskerReceiver extends BroadcastReceiver {
override def onReceive(context: Context, intent: Intent) { override def onReceive(context: Context, intent: Intent) {
val settings = TaskerSettings.fromIntent(intent) val settings = TaskerSettings.fromIntent(intent)
var changed = false
app.profileManager.getProfile(settings.profileId) match { app.profileManager.getProfile(settings.profileId) match {
case Some(_) => app.switchProfile(settings.profileId) case Some(_) => if (app.dataStore.profileId != settings.profileId) {
app.switchProfile(settings.profileId)
changed = true
}
case _ => case _ =>
} }
if (settings.switchOn) Utils.startSsService(context) else Utils.stopSsService(context) if (settings.switchOn) {
Utils.startSsService(context)
if (changed) Utils.reloadSsService(context)
} else Utils.stopSsService(context)
} }
} }
...@@ -94,6 +94,7 @@ object State { ...@@ -94,6 +94,7 @@ object State {
object Action { object Action {
final val SERVICE = "com.github.shadowsocks.SERVICE" final val SERVICE = "com.github.shadowsocks.SERVICE"
final val CLOSE = "com.github.shadowsocks.CLOSE" final val CLOSE = "com.github.shadowsocks.CLOSE"
final val RELOAD = "com.github.shadowsocks.RELOAD"
final val EXTRA_PROFILE_ID = "com.github.shadowsocks.EXTRA_PROFILE_ID" final val EXTRA_PROFILE_ID = "com.github.shadowsocks.EXTRA_PROFILE_ID"
} }
...@@ -33,7 +33,7 @@ import android.view.View.MeasureSpec ...@@ -33,7 +33,7 @@ import android.view.View.MeasureSpec
import android.view.{Gravity, View, Window} import android.view.{Gravity, View, Window}
import android.widget.Toast import android.widget.Toast
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.{BuildConfig, ShadowsocksRunnerService} import com.github.shadowsocks.{BuildConfig, ShadowsocksNatService, ShadowsocksVpnService}
import org.xbill.DNS._ import org.xbill.DNS._
import scala.collection.JavaConversions._ import scala.collection.JavaConversions._
...@@ -137,11 +137,11 @@ object Utils { ...@@ -137,11 +137,11 @@ object Utils {
} }
def startSsService(context: Context) { def startSsService(context: Context) {
val intent = new Intent(context, classOf[ShadowsocksRunnerService]) val intent =
if (Build.VERSION.SDK_INT >= 26) context.startForegroundService(intent) new Intent(context, if (app.dataStore.isNAT) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService])
else context.startService(intent) if (Build.VERSION.SDK_INT >= 26) context.startForegroundService(intent) else context.startService(intent)
} }
def reloadSsService(context: Context): Unit = context.sendBroadcast(new Intent(Action.RELOAD))
def stopSsService(context: Context) { def stopSsService(context: Context) {
val intent = new Intent(Action.CLOSE) val intent = new Intent(Action.CLOSE)
context.sendBroadcast(intent) 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