Commit 8538bd00 authored by chenhuaqing's avatar chenhuaqing

adapt 'Always-on VPN' option for android 9 and up

parent 5c3e490f
......@@ -75,6 +75,8 @@ dependencies {
api("androidx.fragment:fragment-ktx:1.3.5")
api("com.google.android.material:material:1.4.0")
api("com.android.volley:volley:1.2.0")
api("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion")
api("androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycleVersion")
api("androidx.preference:preference:1.1.1")
......
package com.github.shadowsocks.bg
import android.os.Build
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.toolbox.RequestFuture
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import com.github.shadowsocks.Core
import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.google.gson.JsonStreamParser
import timber.log.Timber
import java.io.File
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
object AuthManager {
private val queue: RequestQueue = Volley.newRequestQueue(Core.app.applicationContext)
fun initAuthProfile() {
val profileId = 1L
val profile = ProfileManager.getProfile(profileId) ?: Profile()
profile.token = "e38daf39db56471cb8bfe2776b24059c"
profile.remotePort = 8390
profile.proxyApps = true
val proxyGames = mutableSetOf(
"com.sec.android.app.sbrowser",
"com.nexon.maplem.global",
"jp.co.cygames.umamusume",
"com.ea.gp.apexlegendsmobilefps",
"jp.konami.duellinks",
"com.riotgames.league.wildrift",
"com.garena.game.codm",
"com.google.android.configupdater",
"com.miHoYo.GenshinImpact",
"com.rekoo.pubgm",
"com.pubg.krmobile",
"com.tgc.sky.android",
"com.YoStarJP.Arknights",
"com.riotgames.league.wildrifttw"
)
proxyGames.addAll(profile.individual.split("\n"))
Timber.d("start vpn with games: $proxyGames")
profile.individual = proxyGames.joinToString("\n")
profile.route = Acl.BYPASS_LAN
profile.password = "barfoo!"
profile.method = "none"
profile.beatsInterval = 60
ProfileManager.updateProfile(profile)
Timber.d("init auth config: ${profile.toJson()}")
}
fun updateConfig(data: BaseService.Data) {
if (data.state != BaseService.State.Connected) {
return
}
thread(name = "update-config") {
val context =
if (Build.VERSION.SDK_INT < 24 || Core.user.isUserUnlocked) Core.app else Core.deviceStorage
val configFile = File(context.noBackupFilesDir, BaseService.CONFIG_FILE)
val configJson = JsonStreamParser(configFile.readText()).asSequence()
.single().asJsonObject
val tokenFuture: RequestFuture<String> = RequestFuture.newFuture()
val retryLimit = 3
var reqCount = 1
while (data.state == BaseService.State.Connected) {
val tokenRequest =
StringRequest(
Request.Method.GET,
"http://10.3.0.34:9000",
tokenFuture,
tokenFuture
)
queue.add(tokenRequest)
val token = try {
tokenFuture.get()
} catch (e: Exception) {
Timber.e(e, "get latest config failed")
if (reqCount == retryLimit) {
Core.stopService()
return@thread
}
reqCount++
Thread.sleep(TimeUnit.SECONDS.toMillis(3))
continue
}
configJson.addProperty("token", token)
Timber.d("recv latest config: $configJson")
configFile.writeText(configJson.toString())
Thread.sleep(TimeUnit.SECONDS.toMillis(3))
}
}
}
}
\ No newline at end of file
......@@ -96,6 +96,7 @@ object BaseService {
if (state == s && msg == null) return
binder.stateChanged(s, msg)
state = s
AuthManager.updateConfig(this)
}
}
......
......@@ -153,6 +153,7 @@ class VpnService : BaseVpnService(), BaseService.Interface {
override suspend fun openConnection(url: URL) = DefaultNetworkListener.get().openConnection(url)
override suspend fun startProcesses() {
AuthManager.initAuthProfile()
worker = ProtectWorker().apply { start() }
super.startProcesses()
sendFd(startVpn())
......
......@@ -25,5 +25,4 @@ dependencies {
implementation("com.takisoft.preferencex:preferencex-simplemenu:1.1.0")
implementation("com.twofortyfouram:android-plugin-api-for-locale:1.0.4")
implementation("me.zhanghai.android.fastscroll:library:1.1.7")
implementation("com.android.volley:volley:1.2.0")
}
......@@ -21,7 +21,6 @@
package com.github.shadowsocks
import android.content.ActivityNotFoundException
import android.os.Build
import android.os.Bundle
import android.os.RemoteException
import android.view.KeyCharacterMap
......@@ -37,19 +36,11 @@ import androidx.core.net.toUri
import androidx.core.view.*
import androidx.drawerlayout.widget.DrawerLayout
import androidx.preference.PreferenceDataStore
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.toolbox.RequestFuture
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.acl.CustomRulesFragment
import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.ShadowsocksConnection
import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import com.github.shadowsocks.subscription.SubscriptionFragment
......@@ -63,11 +54,6 @@ import com.google.android.material.snackbar.Snackbar
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase
import com.google.gson.JsonStreamParser
import timber.log.Timber
import java.io.File
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPreferenceDataStoreChangeListener,
NavigationView.OnNavigationItemSelectedListener {
......@@ -131,69 +117,9 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
private fun toggle() = if (state.canStop) {
Core.stopService()
} else {
if (state == BaseService.State.Stopped) {
val profileId = 1L
val profile = ProfileManager.getProfile(profileId) ?: Profile()
profile.token = "e38daf39db56471cb8bfe2776b24059c"
profile.remotePort = 8390
profile.proxyApps = true
val proxyGames = mutableSetOf(
"com.sec.android.app.sbrowser",
"com.nexon.maplem.global",
"jp.co.cygames.umamusume",
"com.ea.gp.apexlegendsmobilefps",
"jp.konami.duellinks",
"com.riotgames.league.wildrift"
)
proxyGames.addAll(profile.individual.split("\n"))
profile.individual = proxyGames.joinToString("\n")
profile.route = Acl.BYPASS_LAN
profile.password = "barfoo!"
profile.method = "none"
profile.beatsInterval = 60
ProfileManager.updateProfile(profile)
Timber.d("update to latest config: ${profile.toJson()}")
}
connect.launch(null)
}
private lateinit var queue: RequestQueue
private fun stateListener(state: BaseService.State) {
if (state == BaseService.State.Connected) {
thread(name = "update-config") {
val context =
if (Build.VERSION.SDK_INT < 24 || Core.user.isUserUnlocked) Core.app else Core.deviceStorage
val configFile = File(context.noBackupFilesDir, BaseService.CONFIG_FILE)
val configJson = JsonStreamParser(configFile.readText()).asSequence()
.single().asJsonObject
val tokenFuture: RequestFuture<String> = RequestFuture.newFuture()
val retryLimit = 3
var reqCount = 1
while (state == BaseService.State.Connected) {
val tokenRequest =
StringRequest(Request.Method.GET, "http://10.3.0.34:9000", tokenFuture, tokenFuture)
queue.add(tokenRequest)
val token = try {
tokenFuture.get()
} catch (e: Exception) {
Timber.e(e, "get latest config failed")
if (reqCount == retryLimit) {
Core.stopService()
return@thread
}
reqCount++
Thread.sleep(TimeUnit.SECONDS.toMillis(3))
continue
}
configJson.addProperty("token", token)
Timber.d("recv latest config: $configJson")
configFile.writeText(configJson.toString())
Thread.sleep(TimeUnit.SECONDS.toMillis(3))
}
}
}
}
private val connection = ShadowsocksConnection(true)
override fun onServiceConnected(service: IShadowsocksService) = changeState(try {
BaseService.State.values()[service.state]
......@@ -226,9 +152,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
displayFragment(ProfilesFragment())
}
queue = Volley.newRequestQueue(this)
stateListener = this::stateListener
fab = findViewById(R.id.fab)
fab.initProgress(findViewById(R.id.fabProgress))
fab.setOnClickListener { toggle() }
......
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