in core/src/main/java/com/github/shadowsocks/bg/BaseService.kt [323:392]
fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = data
if (data.state != State.Stopped) return Service.START_NOT_STICKY
val expanded = Core.currentProfile
this as Context
if (expanded == null) {
// gracefully shutdown: https://stackoverflow.com/q/47337857/2245107
data.notification = createNotification("")
stopRunner(false, getString(R.string.profile_empty))
return Service.START_NOT_STICKY
}
val (profile, fallback) = expanded
profile.name = profile.formattedName // save name for later queries
val proxy = ProxyInstance(profile)
data.proxy = proxy
data.udpFallback = if (fallback == null) null else ProxyInstance(fallback, profile.route)
BootReceiver.enabled = DataStore.persistAcrossReboot
if (!data.closeReceiverRegistered) {
registerReceiver(data.closeReceiver, IntentFilter().apply {
addAction(Action.RELOAD)
addAction(Intent.ACTION_SHUTDOWN)
addAction(Action.CLOSE)
}, "$packageName.SERVICE", null)
data.closeReceiverRegistered = true
}
data.notification = createNotification(profile.formattedName)
Firebase.analytics.logEvent("start") { param(FirebaseAnalytics.Param.METHOD, tag) }
data.changeState(State.Connecting)
data.connectingJob = GlobalScope.launch(Dispatchers.Main) {
try {
Executable.killAll() // clean up old processes
preInit()
proxy.init(this@Interface)
data.udpFallback?.init(this@Interface)
if (profile.route == Acl.CUSTOM_RULES) try {
withContext(Dispatchers.IO) {
Acl.customRules.flatten(10, this@Interface::openConnection).also {
Acl.save(Acl.CUSTOM_RULES, it)
}
}
} catch (e: IOException) {
throw ExpectedExceptionWrapper(e)
}
data.processes = GuardedProcessPool {
Timber.w(it)
stopRunner(false, it.readableMessage)
}
startProcesses()
proxy.scheduleUpdate()
data.udpFallback?.scheduleUpdate()
data.changeState(State.Connected)
} catch (_: CancellationException) {
// if the job was cancelled, it is canceller's responsibility to call stopRunner
} catch (_: UnknownHostException) {
stopRunner(false, getString(R.string.invalid_server))
} catch (exc: Throwable) {
if (exc is ExpectedException) Timber.d(exc) else Timber.w(exc)
stopRunner(false, "${getString(R.string.service_failed)}: ${exc.readableMessage}")
} finally {
data.connectingJob = null
}
}
return Service.START_NOT_STICKY
}