Commit 2677cee4 authored by chenhuaqing's avatar chenhuaqing

code refactor

parent 116dee96
...@@ -7,6 +7,14 @@ data class NetState( ...@@ -7,6 +7,14 @@ data class NetState(
var accNetDelay: Int, var accNetDelay: Int,
//丢包率 //丢包率
var lossRate: String var lossRate: String,
var pingWifiLatency: Int = -1,
var pingWifiTotal: Int = -1,
var pingWifiLoss: Int = -1,
var pingCellularLatency: Int = -1,
var pingCellularTotal: Int = -1,
var pingCellularLoss: Int = -1,
) )
\ No newline at end of file
...@@ -5,10 +5,11 @@ import androidx.lifecycle.ViewModelProvider ...@@ -5,10 +5,11 @@ import androidx.lifecycle.ViewModelProvider
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import com.ccwangluo.accelerator.databinding.FragmentBtnNavBinding import com.ccwangluo.accelerator.databinding.FragmentBtnNavBinding
import com.ccwangluo.accelerator.ui.home.AccelertorFragment import com.ccwangluo.accelerator.ui.home.AcceleratorFragment
import com.ccwangluo.accelerator.ui.home.AccelertorViewModel import com.ccwangluo.accelerator.ui.home.AccelertorViewModel
import com.ccwangluo.accelerator.ui.home.MineFragment import com.ccwangluo.accelerator.ui.home.MineFragment
import com.ccwangluo.accelerator.ui.home.NewsFragment import com.ccwangluo.accelerator.ui.home.NewsFragment
import com.ccwangluo.accelerator.utils.AcceleratorUtils
import com.ccwangluo.accelerator.utils.LoginUtils import com.ccwangluo.accelerator.utils.LoginUtils
import com.ccwangluo.accelerator.utils.datareport.DataRePortUtils import com.ccwangluo.accelerator.utils.datareport.DataRePortUtils
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
...@@ -22,12 +23,12 @@ import com.xuexiang.xutil.common.CollectionUtils ...@@ -22,12 +23,12 @@ import com.xuexiang.xutil.common.CollectionUtils
@Page(name = "main") @Page(name = "main")
class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener, class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener,
BottomNavigationView.OnNavigationItemSelectedListener { BottomNavigationView.OnNavigationItemSelectedListener {
private lateinit var accelertorViewModel: AccelertorViewModel private lateinit var acceleratorViewModel: AccelertorViewModel
lateinit var binding: FragmentBtnNavBinding lateinit var binding: FragmentBtnNavBinding
override fun inflateView(inflater: LayoutInflater?, container: ViewGroup?): View { override fun inflateView(inflater: LayoutInflater?, container: ViewGroup?): View {
binding = FragmentBtnNavBinding.inflate(inflater!!, container, false) binding = FragmentBtnNavBinding.inflate(inflater!!, container, false)
accelertorViewModel = acceleratorViewModel =
ViewModelProvider(requireActivity()).get(AccelertorViewModel::class.java) ViewModelProvider(requireActivity()).get(AccelertorViewModel::class.java)
return binding.root return binding.root
} }
...@@ -39,7 +40,7 @@ class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener ...@@ -39,7 +40,7 @@ class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener
var titles = arrayOf("加速", "资讯", "我的") var titles = arrayOf("加速", "资讯", "我的")
var fragments = arrayOf( var fragments = arrayOf(
AccelertorFragment.newInstance(), AcceleratorFragment.newInstance(),
NewsFragment.newInstance(), NewsFragment.newInstance(),
MineFragment.newInstance() MineFragment.newInstance()
) )
...@@ -55,6 +56,8 @@ class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener ...@@ -55,6 +56,8 @@ class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener
binding.viewPager.setOffscreenPageLimit(titles.size - 1); binding.viewPager.setOffscreenPageLimit(titles.size - 1);
binding.viewPager.adapter = adapter binding.viewPager.adapter = adapter
binding.viewPager.setNoScroll(true) binding.viewPager.setNoScroll(true)
AcceleratorUtils.initConnect(fragments[0] as AcceleratorFragment)
} }
override fun initListeners() { override fun initListeners() {
...@@ -78,7 +81,7 @@ class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener ...@@ -78,7 +81,7 @@ class BottomNavigationFragment : XPageFragment(), ViewPager.OnPageChangeListener
LoginUtils.token?.let { LoginUtils.token?.let {
LoginUtils.getUserProfile(this) { LoginUtils.getUserProfile(this) {
it?.let { it?.let {
accelertorViewModel.userProfile.postValue(it) acceleratorViewModel.userProfile.postValue(it)
} }
} }
} }
......
...@@ -45,14 +45,14 @@ class MainActivity() : XPageActivity(), TencentLocationListener { ...@@ -45,14 +45,14 @@ class MainActivity() : XPageActivity(), TencentLocationListener {
} }
} }
private lateinit var accelertorViewModel: AccelertorViewModel private lateinit var acceleratorViewModel: AccelertorViewModel
private var mSensorEventListener: SensorEventListener? = null private var mSensorEventListener: SensorEventListener? = null
private var mSensorManager: SensorManager? = null private var mSensorManager: SensorManager? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
accelertorViewModel = acceleratorViewModel =
ViewModelProvider(this).get(AccelertorViewModel::class.java) ViewModelProvider(this).get(AccelertorViewModel::class.java)
// app 启动 // app 启动
if (SettingSPUtils.lastAPPOpenReportTime == 0L || if (SettingSPUtils.lastAPPOpenReportTime == 0L ||
...@@ -83,14 +83,14 @@ class MainActivity() : XPageActivity(), TencentLocationListener { ...@@ -83,14 +83,14 @@ class MainActivity() : XPageActivity(), TencentLocationListener {
getPermission() getPermission()
} }
AcceleratorUtils.initConnect(this, connect) AcceleratorUtils.initContext(this, connect)
mSensorManager = getSystemService(SENSOR_SERVICE) as SensorManager mSensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
mSensorEventListener = JCVideoPlayer.JCAutoFullscreenListener() mSensorEventListener = JCVideoPlayer.JCAutoFullscreenListener()
DataRePortUtils.report("st_app_open") DataRePortUtils.report("st_app_open")
} }
fun getPermission() { private fun getPermission() {
PermissionUtils.permission(Manifest.permission.READ_PHONE_STATE, PermissionUtils.permission(Manifest.permission.READ_PHONE_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
......
...@@ -12,6 +12,7 @@ import android.view.animation.RotateAnimation ...@@ -12,6 +12,7 @@ import android.view.animation.RotateAnimation
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.ccwangluo.accelerator.R import com.ccwangluo.accelerator.R
import com.ccwangluo.accelerator.databinding.FragmentQuickBinding import com.ccwangluo.accelerator.databinding.FragmentQuickBinding
import com.ccwangluo.accelerator.model.NetState
import com.ccwangluo.accelerator.ui.dialog.CommonDialog import com.ccwangluo.accelerator.ui.dialog.CommonDialog
import com.ccwangluo.accelerator.ui.dialog.MemberDialog import com.ccwangluo.accelerator.ui.dialog.MemberDialog
import com.ccwangluo.accelerator.ui.web.CommonWebViewFragment import com.ccwangluo.accelerator.ui.web.CommonWebViewFragment
...@@ -30,11 +31,10 @@ import com.xuexiang.xpage.utils.TitleBar ...@@ -30,11 +31,10 @@ import com.xuexiang.xpage.utils.TitleBar
import com.xuexiang.xui.widget.progress.CircleProgressView import com.xuexiang.xui.widget.progress.CircleProgressView
import com.xuexiang.xutil.app.AppUtils import com.xuexiang.xutil.app.AppUtils
class AcceleratorFragment : XPageFragment() {
class AccelertorFragment : XPageFragment() {
private lateinit var binding: FragmentQuickBinding private lateinit var binding: FragmentQuickBinding
private lateinit var accelertorViewModel: AccelertorViewModel private lateinit var acceleratorViewModel: AccelertorViewModel
private var acc_timer = 0L private var accTimer = 0L
override fun initTitleBar(): TitleBar? { override fun initTitleBar(): TitleBar? {
//不使用@Page标注的一定要注意覆盖这个方法 //不使用@Page标注的一定要注意覆盖这个方法
...@@ -43,16 +43,25 @@ class AccelertorFragment : XPageFragment() { ...@@ -43,16 +43,25 @@ class AccelertorFragment : XPageFragment() {
var state: BaseService.State? = null var state: BaseService.State? = null
fun updateNetState(netState: NetState) {
acceleratorViewModel.netState.postValue(netState)
accTimer = AcceleratorUtils.getAccTime()
}
fun changeState(state: BaseService.State) {
acceleratorViewModel.accState.postValue(state)
}
override fun inflateView(inflater: LayoutInflater?, container: ViewGroup?): View { override fun inflateView(inflater: LayoutInflater?, container: ViewGroup?): View {
binding = FragmentQuickBinding.inflate(inflater!!, container, false) binding = FragmentQuickBinding.inflate(inflater!!, container, false)
accelertorViewModel = acceleratorViewModel =
ViewModelProvider(requireActivity()).get(AccelertorViewModel::class.java) ViewModelProvider(requireActivity()).get(AccelertorViewModel::class.java)
return binding.root return binding.root
} }
override fun initViews() { override fun initViews() {
accelertorViewModel.netState.observe(this, { acceleratorViewModel.netState.observe(this, {
if (it.preNetDelay != -1) { //有网络 if (it.preNetDelay != -1) { //有网络
binding.preDelay1.setText(it.preNetDelay.toString()) binding.preDelay1.setText(it.preNetDelay.toString())
...@@ -99,8 +108,8 @@ class AccelertorFragment : XPageFragment() { ...@@ -99,8 +108,8 @@ class AccelertorFragment : XPageFragment() {
binding.activeNetwork.setText(SysUtils.getActiveNet()) binding.activeNetwork.setText(SysUtils.getActiveNet())
}) })
accelertorViewModel.accState.observe(this, { acceleratorViewModel.accState.observe(this, {
binding.accStateTx.setText(SysUtils.getAccTimeString(acc_timer)) binding.accStateTx.setText(SysUtils.getAccTimeString(accTimer))
state = it state = it
freshData(it) freshData(it)
if (SettingSPUtils.isSmartAccOpen) { if (SettingSPUtils.isSmartAccOpen) {
...@@ -114,7 +123,7 @@ class AccelertorFragment : XPageFragment() { ...@@ -114,7 +123,7 @@ class AccelertorFragment : XPageFragment() {
} }
}) })
accelertorViewModel.userProfile.observe(this, { acceleratorViewModel.userProfile.observe(this, {
if (it.hasUnreadNotice) { if (it.hasUnreadNotice) {
binding.iconNoticeBadge.visibility = View.VISIBLE binding.iconNoticeBadge.visibility = View.VISIBLE
} else { } else {
...@@ -136,7 +145,7 @@ class AccelertorFragment : XPageFragment() { ...@@ -136,7 +145,7 @@ class AccelertorFragment : XPageFragment() {
binding.progressArea.visibility = View.GONE binding.progressArea.visibility = View.GONE
binding.btnQuick.isEnabled = true binding.btnQuick.isEnabled = true
binding.progressViewCircleSmall.progress = 0F binding.progressViewCircleSmall.progress = 0F
AcceleratorUtils.startAccelerator(this@AccelertorFragment) AcceleratorUtils.startAccelerator(this@AcceleratorFragment)
} }
}) })
...@@ -196,7 +205,7 @@ class AccelertorFragment : XPageFragment() { ...@@ -196,7 +205,7 @@ class AccelertorFragment : XPageFragment() {
} }
} }
binding.btnQuick.setOnClickListener { binding.btnQuick.setOnClickListener {
accelertorViewModel.accState.value?.let { acceleratorViewModel.accState.value?.let {
if (it.canStop) { if (it.canStop) {
context?.let { context?.let {
CommonDialog(it).setTitle("加速停止确认") CommonDialog(it).setTitle("加速停止确认")
...@@ -252,7 +261,7 @@ class AccelertorFragment : XPageFragment() { ...@@ -252,7 +261,7 @@ class AccelertorFragment : XPageFragment() {
} }
} }
//用户是否有使用时长 //用户是否有使用时长
LoginUtils.getUserHasTimer(this@AccelertorFragment) { LoginUtils.getUserHasTimer(this@AcceleratorFragment) {
if (it) { if (it) {
binding.btnQuick.isEnabled = false binding.btnQuick.isEnabled = false
startAnim() startAnim()
...@@ -261,7 +270,7 @@ class AccelertorFragment : XPageFragment() { ...@@ -261,7 +270,7 @@ class AccelertorFragment : XPageFragment() {
"st_speed_result", "st_speed_result",
mapOf("result" to 0, "cause" to 3) mapOf("result" to 0, "cause" to 3)
) )
MemberDialog(this@AccelertorFragment).show<MemberDialog>() MemberDialog(this@AcceleratorFragment).show<MemberDialog>()
} }
} }
} else { } else {
...@@ -375,7 +384,7 @@ class AccelertorFragment : XPageFragment() { ...@@ -375,7 +384,7 @@ class AccelertorFragment : XPageFragment() {
LoginUtils.token?.let { LoginUtils.token?.let {
LoginUtils.getUserProfile(this) { LoginUtils.getUserProfile(this) {
it?.let { it?.let {
accelertorViewModel.userProfile.postValue(it) acceleratorViewModel.userProfile.postValue(it)
} }
} }
} }
...@@ -384,7 +393,7 @@ class AccelertorFragment : XPageFragment() { ...@@ -384,7 +393,7 @@ class AccelertorFragment : XPageFragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AcceleratorUtils.registerVipExpireListener { AcceleratorUtils.registerVipExpireListener {
MemberDialog(this@AccelertorFragment).show<MemberDialog>() MemberDialog(this@AcceleratorFragment).show<MemberDialog>()
} }
} }
...@@ -396,18 +405,18 @@ class AccelertorFragment : XPageFragment() { ...@@ -396,18 +405,18 @@ class AccelertorFragment : XPageFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
AcceleratorUtils.registerStateListener { state, l, net -> // AcceleratorUtils.registerStateListener { state, l, net ->
net?.let { // net?.let {
accelertorViewModel.netState.postValue(net) // acceleratorViewModel.netState.postValue(net)
} // }
acc_timer = l // accTimer = l
accelertorViewModel.accState.postValue(state) // acceleratorViewModel.accState.postValue(state)
} // }
} }
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
AcceleratorUtils.unRegisterStateListener() // AcceleratorUtils.unRegisterStateListener()
} }
override fun onResume() { override fun onResume() {
...@@ -462,9 +471,8 @@ class AccelertorFragment : XPageFragment() { ...@@ -462,9 +471,8 @@ class AccelertorFragment : XPageFragment() {
} }
companion object { companion object {
fun newInstance(): AccelertorFragment { fun newInstance(): AcceleratorFragment {
val fragment = AccelertorFragment() return AcceleratorFragment()
return fragment
} }
} }
......
...@@ -4,17 +4,20 @@ import android.annotation.SuppressLint ...@@ -4,17 +4,20 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.RemoteException import android.os.RemoteException
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.ccwangluo.accelerator.model.* import com.ccnet.acc.NetworkAdapter
import com.ccwangluo.accelerator.model.Game
import com.ccwangluo.accelerator.model.GameInfo
import com.ccwangluo.accelerator.model.NetState
import com.ccwangluo.accelerator.ui.dialog.CommonDialog import com.ccwangluo.accelerator.ui.dialog.CommonDialog
import com.ccwangluo.accelerator.ui.home.AcceleratorFragment
import com.ccwangluo.accelerator.utils.datareport.DataRePortUtils import com.ccwangluo.accelerator.utils.datareport.DataRePortUtils
import com.ccwangluo.accelerator.utils.http.HttpGo import com.ccwangluo.accelerator.utils.http.HttpGo
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.ShadowsocksConnection import com.github.shadowsocks.aidl.ShadowsocksConnection
import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.bg.AuthManager import com.github.shadowsocks.bg.AuthManager
import com.github.shadowsocks.bg.AuthManager.ACC_TIME import com.github.shadowsocks.bg.AuthManager.ACC_TIME
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
...@@ -22,9 +25,16 @@ import com.github.shadowsocks.database.Profile ...@@ -22,9 +25,16 @@ import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.hjq.toast.ToastUtils import com.hjq.toast.ToastUtils
import com.xuexiang.xpage.base.XPageActivity
import com.xuexiang.xpage.base.XPageFragment import com.xuexiang.xpage.base.XPageFragment
import com.xuexiang.xutil.common.StringUtils import com.xuexiang.xutil.common.StringUtils
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.net.InetAddress
import java.util.concurrent.TimeUnit
/** /**
...@@ -37,22 +47,67 @@ object AcceleratorUtils { ...@@ -37,22 +47,67 @@ object AcceleratorUtils {
private lateinit var connect: ActivityResultLauncher<Void?> private lateinit var connect: ActivityResultLauncher<Void?>
private lateinit var context: AppCompatActivity private lateinit var context: XPageActivity
private lateinit var acceleratorFragment: AcceleratorFragment
// service // service
var state = BaseService.State.Idle var state = BaseService.State.Idle
private set
//游戏 //游戏
var game: GameInfo? = null var game: GameInfo? = null
var stateListener: (BaseService.State, Long, NetState?) -> Unit = { state, l, net -> private val stateListener: (BaseService.State) -> Unit = { state ->
if (state == BaseService.State.Connected) {
game?.let {
val gameServerAndPort = it.boosterServer.split(":")
DataStore.pingAccHost = gameServerAndPort[0]
DataStore.pingAccPort = gameServerAndPort[1].toInt()
context.lifecycleScope.launch(Dispatchers.IO) {
while (AcceleratorUtils.state == BaseService.State.Connected) {
netState.preNetDelay =
NetUtils.tcpPing(gameServerAndPort[0], gameServerAndPort[1].toInt())
.toInt()
// val delay =
// NetUtils.tcpPing(serverPort[0], serverPort[1].toInt(), DataStore.portProxy)
// .toInt()
// netState.accNetDelay = delay
LoginUtils.getUserHasTimer { haveTime ->
if (!haveTime) { //没有加速时长关闭服务
stopAccelerator()
}
}
netState.accNetDelay =
if (netState.accNetDelay > netState.preNetDelay && netState.preNetDelay != -1) {
netState.preNetDelay
} else {
netState.accNetDelay
}
netState.lossRate = if (netState.accNetDelay == -1) "100" else "0"
acceleratorFragment.updateNetState(netState)
delay(1000)
}
}
}
} else {
netState.accNetDelay = -1
netState.lossRate = if (netState.preNetDelay == -1) "100" else "0"
acceleratorFragment.updateNetState(netState)
}
acceleratorFragment.changeState(state)
} }
var vipExpireListener: () -> Unit = { -> var vipExpireListener: () -> Unit = { ->
} }
var netState = NetState(-1, -1, "100")
fun getGameList(xPageFragment: XPageFragment, callback: (Boolean) -> Unit) { fun getGameList(xPageFragment: XPageFragment, callback: (Boolean) -> Unit) {
game?.let { game?.let {
callback(true) callback(true)
...@@ -64,8 +119,8 @@ object AcceleratorUtils { ...@@ -64,8 +119,8 @@ object AcceleratorUtils {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
callback(true) callback(true)
} }
game?.let { game?.let { gameInfo ->
AuthManager.gameId = it.id.toString() AuthManager.gameId = gameInfo.id.toString()
} }
} }
} }
...@@ -73,44 +128,26 @@ object AcceleratorUtils { ...@@ -73,44 +128,26 @@ object AcceleratorUtils {
} }
fun initContext(context: XPageActivity, connect: ActivityResultLauncher<Void?>) {
fun initConnect(context: AppCompatActivity, connect: ActivityResultLauncher<Void?>) {
this.context = context this.context = context
this.connect = connect this.connect = connect
connection.connect(context, shadowsocksConnection) NetworkAdapter.initNetworks(context)
ProfileManager.ensureNotEmpty() ProfileManager.ensureNotEmpty()
context.lifecycleScope.launch(Dispatchers.IO) { context.lifecycleScope.launch(Dispatchers.IO) {
NetUtils.init()
while (true) { while (true) {
game?.let { testLatency()
var net = NetState(-1, -1, "100") delay(TimeUnit.SECONDS.toMillis(DataStore.pingIntervalSec.toLong()))
if (state == BaseService.State.Connected) {
val split = it.boosterServer.split(":")
net.preNetDelay = NetUtils.tcpPing(split[0], split[1].toInt()).toInt()
val delay =
NetUtils.tcpPing(split[0], split[1].toInt(), DataStore.portProxy)
.toInt()
net.accNetDelay = delay
LoginUtils.getUserHasTimer {
if (!it) { //没有加速时长关闭服务
stopAccelerator()
}
}
net.accNetDelay =
if (net.accNetDelay > net.preNetDelay && net.preNetDelay != -1) net.preNetDelay else net.accNetDelay
net.lossRate = if (net.accNetDelay == -1) "100" else "0"
} else {
net.accNetDelay = -1
net.lossRate = if (net.preNetDelay == -1) "100" else "0"
}
withContext(Dispatchers.Main) {
stateListener(state, getAccTime(), net)
}
delay(1000)
}
} }
} }
} }
fun initConnect(acceleratorFragment: AcceleratorFragment) {
this.acceleratorFragment = acceleratorFragment
connection.connect(context, shadowsocksConnection)
}
fun checkVpn() { fun checkVpn() {
if (AuthManager.Last_ACC_TIme == 0L || System.currentTimeMillis() - AuthManager.Last_ACC_TIme > 2 * 3600 * 1000) { if (AuthManager.Last_ACC_TIme == 0L || System.currentTimeMillis() - AuthManager.Last_ACC_TIme > 2 * 3600 * 1000) {
return return
...@@ -131,37 +168,67 @@ object AcceleratorUtils { ...@@ -131,37 +168,67 @@ object AcceleratorUtils {
fun startAccelerator(xPageFragment: XPageFragment) { fun startAccelerator(xPageFragment: XPageFragment) {
game?.let { game?.let {
initAuthProfile(xPageFragment) initAuthProfile(xPageFragment)
val serverPort = it.boosterServer.split(":")
DataStore.pingAccHost = serverPort[0]
DataStore.pingAccPort = serverPort[1].toInt()
// context.lifecycleScope.launch(Dispatchers.IO) {
// while (true) {
// val net = NetState(-1, -1, "100")
// if (state == BaseService.State.Connected) {
// net.preNetDelay =
// NetUtils.tcpPing(serverPort[0], serverPort[1].toInt()).toInt()
// val delay =
// NetUtils.tcpPing(
// serverPort[0],
// serverPort[1].toInt(),
// DataStore.portProxy
// )
// .toInt()
// net.accNetDelay = delay
// LoginUtils.getUserHasTimer {
// if (!it) { //没有加速时长关闭服务
// stopAccelerator()
// }
// }
// net.accNetDelay =
// if (net.accNetDelay > net.preNetDelay && net.preNetDelay != -1) net.preNetDelay else net.accNetDelay
// net.lossRate = if (net.accNetDelay == -1) "100" else "0"
// } else {
// net.accNetDelay = -1
// net.lossRate = if (net.preNetDelay == -1) "100" else "0"
// }
// withContext(Dispatchers.Main) {
// stateListener(state, getAccTime(), net)
// }
// delay(1000)
// }
// }
} }
} }
fun initAuthProfile(xPageFragment: XPageFragment) { private fun initAuthProfile(xPageFragment: XPageFragment) {
xPageFragment.lifecycleScope.launch(Dispatchers.IO) { xPageFragment.lifecycleScope.launch(Dispatchers.IO) {
val profileId = 1L val profileId = 1L
val profile = ProfileManager.getProfile(profileId) ?: Profile() val profile = ProfileManager.getProfile(profileId) ?: Profile()
profile.proxyApps = true profile.proxyApps = true
val proxyGames = mutableSetOf( val proxyGames = mutableSetOf(
"com.sec.android.app.sbrowser", // "com.android.chrome",
"com.nexon.maplem.global", "com.example.myapplication",
"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", "com.riotgames.league.wildrifttw",
"com.baidu.browser.apps" "com.activision.callofduty.shooter",
"com.pubg.krmobile",
) )
game?.boosterPackageName?.split(",")?.forEach { game?.boosterPackageName?.split(",")?.forEach {
if (!StringUtils.isEmpty(it)) { if (!StringUtils.isEmpty(it)) {
proxyGames.add(it) proxyGames.add(it)
} }
} }
// proxyGames.addAll(profile.individual.split("\n"))
game?.boosterPackageName?.split(",")?.forEach {
if (!StringUtils.isEmpty(it)) {
proxyGames.add(it)
}
}
profile.individual = proxyGames.joinToString("\n") profile.individual = proxyGames.joinToString("\n")
profile.route = Acl.BYPASS_LAN profile.route = Acl.BYPASS_LAN
profile.password = "barfoo!" profile.password = "barfoo!"
...@@ -175,27 +242,27 @@ object AcceleratorUtils { ...@@ -175,27 +242,27 @@ object AcceleratorUtils {
} }
fun registerStateListener(listener: (BaseService.State, Long, NetState?) -> Unit) { // fun registerStateListener(listener: (BaseService.State, Long, NetState?) -> Unit) {
stateListener = listener // stateListener = listener
stateListener(state, getAccTime(), null) // stateListener(state, getAccTime(), null)
} // }
//
fun unRegisterStateListener() { // fun unRegisterStateListener() {
stateListener = { state, l, net -> // stateListener = { state, l, net ->
//
} // }
} // }
fun registerVipExpireListener(listener: () -> Unit) { fun registerVipExpireListener(listener: () -> Unit) {
vipExpireListener = listener vipExpireListener = listener
} }
fun unRegisterVipExpireListener() { fun unRegisterVipExpireListener() {
vipExpireListener = { -> vipExpireListener = { ->
} }
} }
fun stopAccelerator() { fun stopAccelerator() {
Core.stopService() Core.stopService()
...@@ -204,35 +271,43 @@ object AcceleratorUtils { ...@@ -204,35 +271,43 @@ object AcceleratorUtils {
private val shadowsocksConnection = object : ShadowsocksConnection.Callback { private val shadowsocksConnection = object : ShadowsocksConnection.Callback {
override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) { override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) {
AcceleratorUtils.state = state AcceleratorUtils.state = state
stateListener(state, getAccTime(), null) stateListener(state)
if (state == BaseService.State.Stopped && !msg.isNullOrBlank()) { if (state == BaseService.State.Stopped && !msg.isNullOrBlank()) {
DataRePortUtils.report("st_speed_result", mapOf("result" to 0, "cause" to 3.toString())) DataRePortUtils.report(
if("vip到期".equals(msg)||(msg!=null&&msg.startsWith("vip已到期"))){ "st_speed_result",
mapOf("result" to 0, "cause" to 3.toString())
)
if ("vip到期".equals(msg) || (msg != null && msg.startsWith("vip已到期"))) {
vipExpireListener() vipExpireListener()
}else{ } else {
stopAccelerator() stopAccelerator()
ToastUtils.show(msg) ToastUtils.show(msg)
} }
} }
if (state == BaseService.State.Connected && getAccTime() < 100){ if (state == BaseService.State.Connected && getAccTime() < 100) {
DataRePortUtils.report("st_speed_result", mapOf("result" to 1)) DataRePortUtils.report("st_speed_result", mapOf("result" to 1))
} }
} }
override fun onServiceConnected(service: IShadowsocksService) { override fun onServiceConnected(service: IShadowsocksService) {
try { state = try {
state = BaseService.State.values()[service.state] BaseService.State.values()[service.state]
} catch (_: RemoteException) { } catch (_: RemoteException) {
state = BaseService.State.Idle BaseService.State.Idle
} }
stateListener(state, getAccTime(), null) stateListener(state)
} }
override fun onBinderDied() { override fun onBinderDied() {
connection.disconnect(context) connection.disconnect(context)
connection.connect(context, this) connection.connect(context, this)
} }
override fun trafficUpdated(profileId: Long, stats: TrafficStats) {
netState.accNetDelay = stats.latency
// acceleratorFragment.updateTraffic(stats)
}
} }
fun getAccTime(): Long { fun getAccTime(): Long {
...@@ -252,4 +327,43 @@ object AcceleratorUtils { ...@@ -252,4 +327,43 @@ object AcceleratorUtils {
connection.disconnect(context) connection.disconnect(context)
} }
private fun testLatency() {
val networkAutoSwitch = false
if (networkAutoSwitch) {
val wifiLatency: Long = NetworkAdapter.wifiNetwork?.let { wifi ->
try {
val pingAddress = wifi.getByName(DataStore.pingNetReachableHost) // DataStore.pingAccHost
val start = System.currentTimeMillis()
pingAddress.isReachable(DataStore.pingTimeoutMillis)
System.currentTimeMillis() - start
} catch (_: Exception) {
-1L
}
} ?: -1
val cellularLatency: Long = NetworkAdapter.cellularNetwork?.let { wifi ->
try {
val pingAddress = wifi.getByName(DataStore.pingNetReachableHost) // DataStore.pingAccHost
val start = System.currentTimeMillis()
pingAddress.isReachable(DataStore.pingTimeoutMillis)
System.currentTimeMillis() - start
} catch (_: Exception) {
-1L
}
} ?: -1
Timber.d("wifi latency: $wifiLatency, cellular latency: $cellularLatency")
} else {
val currentNetworkLatency = try {
val defaultNetAddress = InetAddress.getByName(DataStore.pingNetReachableHost) // DataStore.pingAccHost
val start = System.currentTimeMillis()
defaultNetAddress.isReachable(DataStore.pingTimeoutMillis)
System.currentTimeMillis() - start
} catch (_: Exception) {
-1L
}
Timber.d("network latency: $currentNetworkLatency")
}
}
} }
\ No newline at end of file
package com.ccwangluo.accelerator.utils package com.ccwangluo.accelerator.utils
import android.net.Network
import com.github.shadowsocks.net.DefaultNetworkListener
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.net.Proxy
import java.net.Socket import java.net.Socket
import java.net.UnknownHostException import java.net.UnknownHostException
import kotlin.coroutines.coroutineContext import kotlin.coroutines.coroutineContext
object NetUtils { object NetUtils {
@Volatile
private var underlyingNetwork: Network? = null
suspend fun init() {
DefaultNetworkListener.start(this) {
underlyingNetwork = it
}
}
suspend fun tcpPing(url: String, port: Int, proxyPort: Int = 0): Long { suspend fun tcpPing(url: String, port: Int, proxyPort: Int = 0): Long {
var time = -1L var time = -1L
for (k in 0 until 2) { for (k in 0 until 2) {
...@@ -25,12 +35,16 @@ object NetUtils { ...@@ -25,12 +35,16 @@ object NetUtils {
return time return time
} }
private fun socketConnectTime(url: String, port: Int, proxyPort: Int): Long { private suspend fun socketConnectTime(url: String, port: Int, proxyPort: Int): Long {
try { try {
val socket = if (proxyPort == 0) { val socket = if (proxyPort == 0) {
Socket() Socket()
} else { } else {
Socket(Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", proxyPort))) val socket = Socket()
DefaultNetworkListener.get().bindSocket(socket)
socket
// Socket(Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", proxyPort)))
} }
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
socket.connect(InetSocketAddress(url, port)) socket.connect(InetSocketAddress(url, port))
......
{
"formatVersion": 1,
"database": {
"version": 30,
"identityHash": "c6293b758903bd4b8084f9d2f0ce9763",
"entities": [
{
"tableName": "Profile",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `token` TEXT, `method` TEXT NOT NULL, `authTimeout` INTEGER NOT NULL, `beatsInterval` INTEGER NOT NULL, `retryLimit` INTEGER NOT NULL, `route` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `proxyApps` INTEGER NOT NULL, `bypass` INTEGER NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `metered` INTEGER NOT NULL, `individual` TEXT NOT NULL, `plugin` TEXT, `udpFallback` INTEGER, `subscription` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remotePort",
"columnName": "remotePort",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "token",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "method",
"columnName": "method",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "authTimeout",
"columnName": "authTimeout",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "beatsInterval",
"columnName": "beatsInterval",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "retryLimit",
"columnName": "retryLimit",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "route",
"columnName": "route",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remoteDns",
"columnName": "remoteDns",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "proxyApps",
"columnName": "proxyApps",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "bypass",
"columnName": "bypass",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "udpdns",
"columnName": "udpdns",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ipv6",
"columnName": "ipv6",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "metered",
"columnName": "metered",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "individual",
"columnName": "individual",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "plugin",
"columnName": "plugin",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "udpFallback",
"columnName": "udpFallback",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c6293b758903bd4b8084f9d2f0ce9763')"
]
}
}
\ No newline at end of file
...@@ -10,4 +10,5 @@ interface IShadowsocksService { ...@@ -10,4 +10,5 @@ interface IShadowsocksService {
void startListeningForBandwidth(in IShadowsocksServiceCallback cb, long timeout); void startListeningForBandwidth(in IShadowsocksServiceCallback cb, long timeout);
oneway void stopListeningForBandwidth(in IShadowsocksServiceCallback cb); oneway void stopListeningForBandwidth(in IShadowsocksServiceCallback cb);
oneway void unregisterCallback(in IShadowsocksServiceCallback cb); oneway void unregisterCallback(in IShadowsocksServiceCallback cb);
oneway void bindNetwork(int networkMode);
} }
package com.ccnet.acc
import android.content.Context
import com.github.shadowsocks.database.Profile
import timber.log.Timber
import java.io.File
object AccVpn {
private const val configFilename = "config.conf"
fun configFile(context: Context): File {
return File(context.filesDir, configFilename)
}
fun writeConfig(context: Context, tunFd: Int, profile: Profile) {
val configContent = """
[General]
loglevel = trace
dns-server = 223.5.5.5
tun-fd = ${tunFd}
[Proxy]
Direct = direct
SS = ss, ${profile.host}, ${profile.remotePort}, encrypt-method=chacha20-ietf-poly1305, password=123456, auth_timeout=${profile.authTimeout}, beats_interval=${profile.beatsInterval}, retry_limit=${profile.retryLimit}, token=${profile.token}
[Rule]
FINAL, SS
"""
val configFile = configFile(context)
Timber.d("writeConfig ${configFile.absolutePath}")
configFile.writeText(configContent)
}
}
\ No newline at end of file
package com.ccnet.acc
class AccVpnService {
companion object {
private val INSTANCE = AccVpnService()
val instance: AccVpnService
get() {
return INSTANCE
}
}
init {
System.loadLibrary("ccnetacc")
}
external fun runAcc(
configPath: String,
protectPath: String,
networkClass: String,
callback: String
)
external fun testLatency(
proxyHost: String,
proxyPort: Int,
testHost: String,
intervalSec: Int,
timeoutMillis: Int,
callback: TestLatencyCallback
): Int
external fun stopAcc()
}
interface TestLatencyCallback {
fun onLatency(latency: Int)
}
\ No newline at end of file
package com.ccnet.acc
import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import timber.log.Timber
import java.io.FileDescriptor
import java.io.IOException
import java.lang.reflect.Field
object NetworkAdapter {
val JNI_CLAZZ_NAME = NetworkAdapter::class.java.name.replace(".", "/")
var wifiNetwork: Network? = null
var cellularNetwork: Network? = null
private var bindNetwork: Network? = null
private val FD_FIELD: Field
val wifiRequest: NetworkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
val cellularRequest: NetworkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
fun initNetworks(context: Context) {
connectivityManager(context).requestNetwork(wifiRequest, object : NetworkCallback() {
override fun onAvailable(network: Network) {
wifiNetwork = network
}
override fun onLost(network: Network) {
wifiNetwork = null
}
})
connectivityManager(context).requestNetwork(cellularRequest, object : NetworkCallback() {
override fun onAvailable(network: Network) {
cellularNetwork = network
}
override fun onLost(network: Network) {
cellularNetwork = null
}
})
}
fun useWifi(context: Context) {
connectivityManager(context).requestNetwork(wifiRequest, object : NetworkCallback() {
override fun onAvailable(network: Network) {
bindNetwork = network
}
override fun onLost(network: Network) {
bindNetwork = null
}
})
}
fun useCellular(context: Context) {
connectivityManager(context).requestNetwork(cellularRequest, object : NetworkCallback() {
override fun onAvailable(network: Network) {
bindNetwork = network
}
override fun onLost(network: Network) {
bindNetwork = null
}
})
}
fun useSystem() {
bindNetwork = null
}
private fun connectivityManager(context: Context): ConnectivityManager {
return context.getSystemService(ConnectivityManager::class.java)
}
@JvmStatic
fun bindSocket(rawFd: Int) {
val bindNetwork = bindNetwork ?: return
val fd = FileDescriptor()
try {
FD_FIELD[fd] = rawFd
NetworkAdapter.bindNetwork!!.bindSocket(fd)
Timber.d("bind socket success")
} catch (e: IllegalAccessException) {
// ignore
Timber.e("bind socket failed $e")
} catch (e: IOException) {
Timber.e("bind socket failed $e")
}
}
init {
try {
@SuppressLint("DiscouragedPrivateApi")
FD_FIELD = FileDescriptor::class.java.getDeclaredField("descriptor")
} catch (e: NoSuchFieldException) {
throw RuntimeException(e)
}
FD_FIELD.isAccessible = true
}
}
\ No newline at end of file
package com.ccnet.acc.aidl
enum class NetworkMode(val code: Int) {
WIFI(0),
CELLULAR(1),
SYSTEM(2),
}
\ No newline at end of file
package com.ccnet.acc.common
object Constants {
const val CONFIG_PREFERENCE = "config_preference"
const val ACC_CONFIG_KEY = "acc_config_key"
}
\ No newline at end of file
package com.ccnet.acc.native
interface NativeCallback {
enum class ErrorCode(val code: Int) {
AuthFailed(1001),
NetworkBindSocketFailed(1002),
NetworkError(1003),
UnknownError(1004),
;
companion object {
private val VALUES = values().associateBy { it.code }
fun from(code: Int): ErrorCode {
return VALUES[code] ?: UnknownError
}
}
}
fun onStart()
fun onError(code: ErrorCode, error: String)
fun onFatal(code: ErrorCode, error: String)
}
\ No newline at end of file
package com.ccnet.acc.native
object VpnNativeDispatcher {
val JNI_CLAZZ_NAME = VpnNativeDispatcher::class.java.name.replace(".", "/")
private val NATIVE_CALLBACKS: MutableMap<String, NativeCallback> = mutableMapOf()
@JvmStatic
fun registerCallback(key: String, listener: NativeCallback) {
NATIVE_CALLBACKS[key] = listener
}
@JvmStatic
private fun onStart() {
for (eventListener in NATIVE_CALLBACKS.values) {
eventListener.onStart()
}
}
@JvmStatic
fun onError(code: Int, error: String) {
for (eventListener in NATIVE_CALLBACKS.values) {
eventListener.onError(NativeCallback.ErrorCode.from(code), error)
}
}
@JvmStatic
fun onFatal(code: Int, error: String) {
for (eventListener in NATIVE_CALLBACKS.values) {
eventListener.onFatal(NativeCallback.ErrorCode.from(code), error)
}
}
}
\ No newline at end of file
...@@ -41,12 +41,6 @@ import kotlinx.coroutines.launch ...@@ -41,12 +41,6 @@ import kotlinx.coroutines.launch
class ShadowsocksConnection(private var listenForDeath: Boolean = false) : ServiceConnection, IBinder.DeathRecipient { class ShadowsocksConnection(private var listenForDeath: Boolean = false) : ServiceConnection, IBinder.DeathRecipient {
companion object { companion object {
val serviceClass get() = VpnService::class.java val serviceClass get() = VpnService::class.java
// when (DataStore.serviceMode) {
// Key.modeProxy -> ProxyService::class
// Key.modeVpn -> VpnService::class
// Key.modeTransproxy -> TransproxyService::class
// else -> throw UnknownError()
// }.java
} }
interface Callback { interface Callback {
......
...@@ -25,15 +25,19 @@ import kotlinx.parcelize.Parcelize ...@@ -25,15 +25,19 @@ import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class TrafficStats( data class TrafficStats(
// Bytes per second // Bytes per second
var txRate: Long = 0L, var txRate: Long = 0L,
var rxRate: Long = 0L, var rxRate: Long = 0L,
// Bytes for the current session // Bytes for the current session
var txTotal: Long = 0L, var txTotal: Long = 0L,
var rxTotal: Long = 0L var rxTotal: Long = 0L,
// round trip latency
var latency: Int = 0,
) : Parcelable { ) : Parcelable {
operator fun plus(other: TrafficStats) = TrafficStats( operator fun plus(other: TrafficStats) = TrafficStats(
txRate + other.txRate, rxRate + other.rxRate, txRate + other.txRate, rxRate + other.rxRate,
txTotal + other.txTotal, rxTotal + other.rxTotal) txTotal + other.txTotal, rxTotal + other.rxTotal, other.latency
)
} }
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import android.os.Build import android.os.Build
import android.util.Log import com.ccnet.acc.AccVpn
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.http.* import com.github.shadowsocks.http.*
import com.github.shadowsocks.preference.DataStore
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonStreamParser import com.google.gson.JsonStreamParser
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
...@@ -94,6 +95,9 @@ object AuthManager { ...@@ -94,6 +95,9 @@ object AuthManager {
profile.host = lineConfig.host profile.host = lineConfig.host
profile.token = lineConfig.token profile.token = lineConfig.token
profile.remotePort = lineConfig.port profile.remotePort = lineConfig.port
DataStore.pingProxyServer = profile.host
DataStore.pingProxyPort = profile.remotePort + 1
Timber.d("start vpn with games: ${profile.individual}, profileId: ${profile.id}, route: ${profile.route}") Timber.d("start vpn with games: ${profile.individual}, profileId: ${profile.id}, route: ${profile.route}")
ProfileManager.updateProfile(profile) ProfileManager.updateProfile(profile)
} else if (lineConfigResult.code == 1001) { } else if (lineConfigResult.code == 1001) {
...@@ -129,6 +133,10 @@ object AuthManager { ...@@ -129,6 +133,10 @@ object AuthManager {
proxyInstance.profile.token = refreshTokenResult.unwrapData().token proxyInstance.profile.token = refreshTokenResult.unwrapData().token
configJson.addProperty("token", proxyInstance.profile.token) configJson.addProperty("token", proxyInstance.profile.token)
configFile.writeText(configJson.toString()) configFile.writeText(configJson.toString())
data.tunFd?.let {
AccVpn.writeConfig(Core.app, it, proxyInstance.profile)
}
return@let
} else if (refreshTokenResult.code == 1001) { } else if (refreshTokenResult.code == 1001) {
failedCount=0 failedCount=0
throw AuthException(refreshTokenResult.error!!) throw AuthException(refreshTokenResult.error!!)
...@@ -157,7 +165,7 @@ object AuthManager { ...@@ -157,7 +165,7 @@ object AuthManager {
): ApiResult<T> { ): ApiResult<T> {
val body: RequestBody = gson.toJson(params).toString().toRequestBody(jsonType) val body: RequestBody = gson.toJson(params).toString().toRequestBody(jsonType)
val request = Request.Builder() val request = Request.Builder()
.url(HttpConfig.baseUrl + url) .url(if (url.startsWith("http")) url else HttpConfig.baseUrl + url)
.post(body) .post(body)
token?.let { token?.let {
request.addHeader("Authorization", it) request.addHeader("Authorization", it)
......
...@@ -28,6 +28,10 @@ import android.os.Build ...@@ -28,6 +28,10 @@ import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.os.RemoteCallbackList import android.os.RemoteCallbackList
import android.os.RemoteException import android.os.RemoteException
import com.ccnet.acc.NetworkAdapter
import com.ccnet.acc.aidl.NetworkMode
import com.ccnet.acc.native.NativeCallback
import com.ccnet.acc.native.VpnNativeDispatcher
import com.github.shadowsocks.BootReceiver import com.github.shadowsocks.BootReceiver
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
...@@ -43,7 +47,6 @@ import com.github.shadowsocks.utils.broadcastReceiver ...@@ -43,7 +47,6 @@ import com.github.shadowsocks.utils.broadcastReceiver
import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.readableMessage
import kotlinx.coroutines.* import kotlinx.coroutines.*
import timber.log.Timber import timber.log.Timber
import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
...@@ -74,6 +77,7 @@ object BaseService { ...@@ -74,6 +77,7 @@ object BaseService {
var proxy: ProxyInstance? = null var proxy: ProxyInstance? = null
var udpFallback: ProxyInstance? = null var udpFallback: ProxyInstance? = null
var localDns: LocalDnsWorker? = null var localDns: LocalDnsWorker? = null
var tunFd: Int? = null
var notification: ServiceNotification? = null var notification: ServiceNotification? = null
val closeReceiver = broadcastReceiver { _, intent -> val closeReceiver = broadcastReceiver { _, intent ->
...@@ -192,6 +196,14 @@ object BaseService { ...@@ -192,6 +196,14 @@ object BaseService {
callbacks.unregister(cb) callbacks.unregister(cb)
} }
override fun bindNetwork(networkMode: Int) {
when (networkMode) {
NetworkMode.WIFI.code -> NetworkAdapter.useWifi(app)
NetworkMode.CELLULAR.code -> NetworkAdapter.useCellular(app)
NetworkMode.SYSTEM.code -> NetworkAdapter.useSystem()
}
}
fun stateChanged(s: State, msg: String?) = launch { fun stateChanged(s: State, msg: String?) = launch {
val profileName = profileName val profileName = profileName
broadcast { it.stateChanged(s.ordinal, profileName, msg) } broadcast { it.stateChanged(s.ordinal, profileName, msg) }
...@@ -229,20 +241,20 @@ object BaseService { ...@@ -229,20 +241,20 @@ object BaseService {
val isVpnService get() = false val isVpnService get() = false
suspend fun startProcesses() { suspend fun startProcesses() {
val context = if (Build.VERSION.SDK_INT < 24 || Core.user.isUserUnlocked) app else Core.deviceStorage // val context = if (Build.VERSION.SDK_INT < 24 || Core.user.isUserUnlocked) app else Core.deviceStorage
val configRoot = context.noBackupFilesDir // val configRoot = context.noBackupFilesDir
val udpFallback = data.udpFallback // val udpFallback = data.udpFallback
data.proxy!!.start(this, // data.proxy!!.start(this,
File(Core.deviceStorage.noBackupFilesDir, "stat_main"), // File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
File(configRoot, CONFIG_FILE), // File(configRoot, CONFIG_FILE),
if (udpFallback == null && data.proxy?.plugin == null) "tcp_and_udp" else "tcp_only") // if (udpFallback == null && data.proxy?.plugin == null) "tcp_and_udp" else "tcp_only")
if (udpFallback?.plugin != null) throw ExpectedExceptionWrapper(IllegalStateException( // if (udpFallback?.plugin != null) throw ExpectedExceptionWrapper(IllegalStateException(
"UDP fallback cannot have plugins")) // "UDP fallback cannot have plugins"))
udpFallback?.start(this, // udpFallback?.start(this,
File(Core.deviceStorage.noBackupFilesDir, "stat_udp"), // File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
File(configRoot, CONFIG_FILE_UDP), // File(configRoot, CONFIG_FILE_UDP),
"udp_only", false) // "udp_only", false)
data.localDns = LocalDnsWorker(this::rawResolver).apply { start() } // data.localDns = LocalDnsWorker(this::rawResolver).apply { start() }
} }
fun startRunner() { fun startRunner() {
...@@ -346,7 +358,8 @@ object BaseService { ...@@ -346,7 +358,8 @@ object BaseService {
data.changeState(State.Connecting) data.changeState(State.Connecting)
data.connectingJob = GlobalScope.launch(Dispatchers.Main) { data.connectingJob = GlobalScope.launch(Dispatchers.Main) {
try { try {
Executable.killAll() // clean up old processes AuthManager.initAuthProfile(profile)
// Executable.killAll() // clean up old processes
preInit() preInit()
if (profile.route == Acl.CUSTOM_RULES) try { if (profile.route == Acl.CUSTOM_RULES) try {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
...@@ -362,12 +375,28 @@ object BaseService { ...@@ -362,12 +375,28 @@ object BaseService {
Timber.w(it) Timber.w(it)
stopRunner(false, it.readableMessage) stopRunner(false, it.readableMessage)
} }
startProcesses() VpnNativeDispatcher.registerCallback(this.javaClass.name,
object : NativeCallback {
override fun onStart() {
data.changeState(State.Connected)
}
data.proxy!!.scheduleUpdate() override fun onError(code: NativeCallback.ErrorCode, error: String) {
data.udpFallback?.scheduleUpdate() }
override fun onFatal(code: NativeCallback.ErrorCode, error: String) {
Timber.e("base service onFatal $code, $error")
if (code == NativeCallback.ErrorCode.NetworkError) {
stopRunner(true, error)
} else {
stopRunner(false, error)
}
}
})
startProcesses()
data.changeState(State.Connected) // data.proxy!!.scheduleUpdate()
// data.udpFallback?.scheduleUpdate()
} catch (_: CancellationException) { } catch (_: CancellationException) {
// if the job was cancelled, it is canceller's responsibility to call stopRunner // if the job was cancelled, it is canceller's responsibility to call stopRunner
} catch (exc: Throwable) { } catch (exc: Throwable) {
......
...@@ -52,7 +52,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -52,7 +52,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
} }
} }
private var configFile: File? = null private var configFile: File? = null
var trafficMonitor: TrafficMonitor? = null var trafficMonitor: TrafficMonitor? = TrafficMonitor(null)
val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) } val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) }
/** /**
...@@ -120,6 +120,8 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -120,6 +120,8 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
Timber.d("start vpn service with acl ${Acl.getFile(route).readText()}") Timber.d("start vpn service with acl ${Acl.getFile(route).readText()}")
} }
// DataStore.outboundBindInterface = "rmnet_data1"
if (DataStore.outboundBindInterface.isNotBlank()) { if (DataStore.outboundBindInterface.isNotBlank()) {
cmd += "--outbound-bind-interface" cmd += "--outbound-bind-interface"
cmd += DataStore.outboundBindInterface cmd += DataStore.outboundBindInterface
......
...@@ -22,38 +22,83 @@ package com.github.shadowsocks.bg ...@@ -22,38 +22,83 @@ package com.github.shadowsocks.bg
import android.net.LocalSocket import android.net.LocalSocket
import android.os.SystemClock import android.os.SystemClock
import com.ccnet.acc.AccVpnService
import com.ccnet.acc.TestLatencyCallback
import com.github.shadowsocks.aidl.TrafficStats import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.net.LocalSocketListener import com.github.shadowsocks.net.LocalSocketListener
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.DirectBoot import com.github.shadowsocks.utils.DirectBoot
import kotlinx.coroutines.CoroutineScope
import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
class TrafficMonitor(statFile: File) { class TrafficMonitor(statFile: File?) {
val thread = object : LocalSocketListener("TrafficMonitor-" + statFile.name, statFile) { interface DisposableTasker {
private val buffer = ByteArray(16) fun shutdown(scope: CoroutineScope)
private val stat = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN) }
override fun acceptInternal(socket: LocalSocket) {
when (val read = socket.inputStream.read(buffer)) { val thread: DisposableTasker = if (statFile != null) {
-1 -> return object : LocalSocketListener("TrafficMonitor-" + statFile.name, statFile),
16 -> { } DisposableTasker {
else -> throw IOException("Unexpected traffic stat length $read") private val buffer = ByteArray(16)
private val stat = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN)
override fun acceptInternal(socket: LocalSocket) {
when (val read = socket.inputStream.read(buffer)) {
-1 -> return
16 -> {
}
else -> throw IOException("Unexpected traffic stat length $read")
}
val tx = stat.getLong(0)
val rx = stat.getLong(8)
if (current.txTotal != tx) {
current.txTotal = tx
dirty = true
}
if (current.rxTotal != rx) {
current.rxTotal = rx
dirty = true
}
} }
val tx = stat.getLong(0) }.apply { start() }
val rx = stat.getLong(8) } else {
if (current.txTotal != tx) { object : Thread("TrafficMonitor-NativePing"), DisposableTasker {
current.txTotal = tx @Volatile
dirty = true private var running = true
override fun run() {
Timber.d("TrafficMonitor-NativePing prepare test ${DataStore.pingAccHost}:${DataStore.pingAccPort}")
while (running) {
val resultCode = AccVpnService.instance.testLatency(
DataStore.pingProxyServer,
DataStore.pingProxyPort,
DataStore.pingAccHost,
DataStore.pingIntervalSec,
DataStore.pingTimeoutMillis,
object : TestLatencyCallback {
override fun onLatency(latency: Int) {
Timber.d("TrafficMonitor-NativeTcpPing test ${current.latency}")
current.latency = latency
dirty = true
}
}
)
if (resultCode != 0) {
Timber.d("TrafficMonitor-NativeTcpPing exited with $resultCode")
}
sleep(DataStore.pingIntervalSec.toLong())
}
} }
if (current.rxTotal != rx) {
current.rxTotal = rx override fun shutdown(scope: CoroutineScope) {
dirty = true running = false
} }
} }.apply { start() }
}.apply { start() } }
val current = TrafficStats() val current = TrafficStats()
var out = TrafficStats() var out = TrafficStats()
......
...@@ -23,6 +23,7 @@ package com.github.shadowsocks.bg ...@@ -23,6 +23,7 @@ package com.github.shadowsocks.bg
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.LocalServerSocket
import android.net.LocalSocket import android.net.LocalSocket
import android.net.LocalSocketAddress import android.net.LocalSocketAddress
import android.net.Network import android.net.Network
...@@ -31,25 +32,27 @@ import android.os.ParcelFileDescriptor ...@@ -31,25 +32,27 @@ import android.os.ParcelFileDescriptor
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.system.OsConstants import android.system.OsConstants
import com.ccnet.acc.AccVpn
import com.ccnet.acc.AccVpnService
import com.ccnet.acc.NetworkAdapter
import com.ccnet.acc.native.VpnNativeDispatcher
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.VpnRequestActivity import com.github.shadowsocks.VpnRequestActivity
import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.github.shadowsocks.net.ConcurrentLocalSocketListener import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.DefaultNetworkListener import com.github.shadowsocks.net.DefaultNetworkListener
import com.github.shadowsocks.net.DnsResolverCompat import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.int import com.github.shadowsocks.utils.int
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import java.nio.ByteBuffer
import kotlin.concurrent.thread
import android.net.VpnService as BaseVpnService import android.net.VpnService as BaseVpnService
class VpnService : BaseVpnService(), BaseService.Interface { class VpnService : BaseVpnService(), BaseService.Interface {
...@@ -59,18 +62,22 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -59,18 +62,22 @@ class VpnService : BaseVpnService(), BaseService.Interface {
private const val PRIVATE_VLAN4_ROUTER = "172.19.0.2" private const val PRIVATE_VLAN4_ROUTER = "172.19.0.2"
private const val PRIVATE_VLAN6_CLIENT = "fdfe:dcba:9876::1" private const val PRIVATE_VLAN6_CLIENT = "fdfe:dcba:9876::1"
private const val PRIVATE_VLAN6_ROUTER = "fdfe:dcba:9876::2" private const val PRIVATE_VLAN6_ROUTER = "fdfe:dcba:9876::2"
private const val PUBLIC_DNS_SERVER = "1.1.1.1"
private fun <T> FileDescriptor.use(block: (FileDescriptor) -> T) = try { private fun <T> FileDescriptor.use(block: (FileDescriptor) -> T) = try {
block(this) block(this)
} finally { } finally {
try { try {
Os.close(this) Os.close(this)
} catch (_: ErrnoException) { } } catch (_: ErrnoException) {
}
} }
} }
private inner class ProtectWorker : ConcurrentLocalSocketListener("ShadowsocksVpnThread", private inner class ProtectWorker : ConcurrentLocalSocketListener(
File(Core.deviceStorage.noBackupFilesDir, "protect_path")) { "ShadowsocksVpnThread",
File(Core.deviceStorage.noBackupFilesDir, "protect_path")
) {
override fun acceptInternal(socket: LocalSocket) { override fun acceptInternal(socket: LocalSocket) {
if (socket.inputStream.read() == -1) return if (socket.inputStream.read() == -1) return
val success = socket.ancillaryFileDescriptors!!.single()!!.use { fd -> val success = socket.ancillaryFileDescriptors!!.single()!!.use { fd ->
...@@ -91,7 +98,8 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -91,7 +98,8 @@ class VpnService : BaseVpnService(), BaseService.Interface {
} }
try { try {
socket.outputStream.write(if (success) 0 else 1) socket.outputStream.write(if (success) 0 else 1)
} catch (_: IOException) { } // ignore connection early close } catch (_: IOException) {
} // ignore connection early close
} }
} }
...@@ -102,21 +110,28 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -102,21 +110,28 @@ class VpnService : BaseVpnService(), BaseService.Interface {
override val data = BaseService.Data(this) override val data = BaseService.Data(this)
override val tag: String get() = "ShadowsocksVpnService" override val tag: String get() = "ShadowsocksVpnService"
override fun createNotification(profileName: String): ServiceNotification = override fun createNotification(profileName: String): ServiceNotification =
ServiceNotification(this, profileName, "service-vpn") ServiceNotification(this, profileName, "service-vpn")
private var conn: ParcelFileDescriptor? = null private var conn: ParcelFileDescriptor? = null
private var worker: ProtectWorker? = null private var worker: ProtectWorker? = null
private var protectJob: Job? = null
private var active = false private var active = false
private var metered = false private var metered = false
@Volatile @Volatile
private var underlyingNetwork: Network? = null private var underlyingNetwork: Network? = null
set(value) { set(value) {
field = value field = value
if (active) setUnderlyingNetworks(underlyingNetworks) if (active) setUnderlyingNetworks(underlyingNetworks)
} }
private val underlyingNetworks get() = private val underlyingNetworks
// clearing underlyingNetworks makes Android 9 consider the network to be metered get() =
if (Build.VERSION.SDK_INT == 28 && metered) null else underlyingNetwork?.let { arrayOf(it) } // clearing underlyingNetworks makes Android 9 consider the network to be metered
if (Build.VERSION.SDK_INT == 28 && metered) null else underlyingNetwork?.let {
arrayOf(
it
)
}
override fun onBind(intent: Intent) = when (intent.action) { override fun onBind(intent: Intent) = when (intent.action) {
SERVICE_INTERFACE -> super<BaseVpnService>.onBind(intent) SERVICE_INTERFACE -> super<BaseVpnService>.onBind(intent)
...@@ -127,10 +142,13 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -127,10 +142,13 @@ class VpnService : BaseVpnService(), BaseService.Interface {
override fun killProcesses(scope: CoroutineScope) { override fun killProcesses(scope: CoroutineScope) {
super.killProcesses(scope) super.killProcesses(scope)
AccVpnService.instance.stopAcc()
active = false active = false
scope.launch { DefaultNetworkListener.stop(this) } scope.launch { DefaultNetworkListener.stop(this) }
worker?.shutdown(scope) worker?.shutdown(scope)
worker = null worker = null
protectJob?.cancel()
protectJob = null
conn?.close() conn?.close()
conn = null conn = null
} }
...@@ -138,7 +156,12 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -138,7 +156,12 @@ class VpnService : BaseVpnService(), BaseService.Interface {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (DataStore.serviceMode == Key.modeVpn) { if (DataStore.serviceMode == Key.modeVpn) {
if (prepare(this) != null) { if (prepare(this) != null) {
startActivity(Intent(this, VpnRequestActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) startActivity(
Intent(
this,
VpnRequestActivity::class.java
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} else { } else {
return super<BaseService.Interface>.onStartCommand(intent, flags, startId) return super<BaseService.Interface>.onStartCommand(intent, flags, startId)
} }
...@@ -149,60 +172,64 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -149,60 +172,64 @@ class VpnService : BaseVpnService(), BaseService.Interface {
override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it } override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it }
override suspend fun rawResolver(query: ByteArray) = override suspend fun rawResolver(query: ByteArray) =
// no need to listen for network here as this is only used for forwarding local DNS queries. // no need to listen for network here as this is only used for forwarding local DNS queries.
// retries should be attempted by client. // retries should be attempted by client.
DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query) DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query)
override suspend fun openConnection(url: URL) = DefaultNetworkListener.get().openConnection(url) override suspend fun openConnection(url: URL) = DefaultNetworkListener.get().openConnection(url)
override suspend fun startProcesses() { override suspend fun startProcesses() {
worker = ProtectWorker().apply { start() } // worker = ProtectWorker().apply { start() }
super.startProcesses() // super.startProcesses()
sendFd(startVpn()) startVpn()
// sendFd(startVpn())
} }
override val isVpnService get() = true override val isVpnService get() = true
private suspend fun startVpn(): FileDescriptor { private fun startVpn(): FileDescriptor {
val profile = data.proxy!!.profile val profile = data.proxy!!.profile
val builder = Builder() val builder = Builder()
.setConfigureIntent(Core.configureIntent(this)) .setConfigureIntent(Core.configureIntent(this))
.setSession(profile.formattedName) .setSession(profile.formattedName)
.setMtu(VPN_MTU) .setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN4_CLIENT, 30) .addAddress(PRIVATE_VLAN4_CLIENT, 30)
.addDnsServer(PRIVATE_VLAN4_ROUTER) .addDnsServer(PUBLIC_DNS_SERVER)
if (profile.ipv6) builder.addAddress(PRIVATE_VLAN6_CLIENT, 126) if (profile.ipv6) builder.addAddress(PRIVATE_VLAN6_CLIENT, 126)
if (profile.proxyApps) { if (profile.proxyApps) {
val me = packageName val me = packageName
profile.individual.split('\n') profile.individual.split('\n')
.filter { it != me } .filter { it != me }
.forEach { .forEach {
try { try {
if (profile.bypass) builder.addDisallowedApplication(it) if (profile.bypass) builder.addDisallowedApplication(it)
else builder.addAllowedApplication(it) else builder.addAllowedApplication(it)
} catch (ex: PackageManager.NameNotFoundException) { } catch (ex: PackageManager.NameNotFoundException) {
Timber.w(ex) Timber.w(ex)
}
} }
}
// if (!profile.bypass) builder.addAllowedApplication(me) // if (!profile.bypass) builder.addAllowedApplication(me)
} }
when (profile.route) { // when (profile.route) {
Acl.ALL, Acl.BYPASS_CHN, Acl.CUSTOM_RULES -> { // Acl.ALL, Acl.BYPASS_CHN, Acl.CUSTOM_RULES -> {
builder.addRoute("0.0.0.0", 0) // builder.addRoute("0.0.0.0", 0)
if (profile.ipv6) builder.addRoute("::", 0) // if (profile.ipv6) builder.addRoute("::", 0)
} // }
else -> { // else -> {
resources.getStringArray(R.array.bypass_private_route).forEach { // resources.getStringArray(R.array.bypass_private_route).forEach {
val subnet = Subnet.fromString(it)!! // val subnet = Subnet.fromString(it)!!
builder.addRoute(subnet.address.hostAddress, subnet.prefixSize) // builder.addRoute(subnet.address.hostAddress, subnet.prefixSize)
} // }
builder.addRoute(PRIVATE_VLAN4_ROUTER, 32) // builder.addRoute(PRIVATE_VLAN4_ROUTER, 32)
// https://issuetracker.google.com/issues/149636790 // // https://issuetracker.google.com/issues/149636790
if (profile.ipv6) builder.addRoute("2000::", 3) // if (profile.ipv6) builder.addRoute("2000::", 3)
} // }
} // }
builder.addRoute("0.0.0.0", 0)
if (profile.ipv6) builder.addRoute("::", 0)
metered = profile.metered metered = profile.metered
active = true // possible race condition here? active = true // possible race condition here?
...@@ -211,46 +238,93 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -211,46 +238,93 @@ class VpnService : BaseVpnService(), BaseService.Interface {
val conn = builder.establish() ?: throw NullConnectionException() val conn = builder.establish() ?: throw NullConnectionException()
this.conn = conn this.conn = conn
data.tunFd = conn.fd
val cmd = arrayListOf(File(applicationInfo.nativeLibraryDir, Executable.TUN2SOCKS).absolutePath, val protectPath =
"--netif-ipaddr", PRIVATE_VLAN4_ROUTER, File.createTempFile("leaf_vpn_socket_protect", ".sock", cacheDir).absolutePath
"--socks-server-addr", "${DataStore.listenAddress}:${DataStore.portProxy}", protectJob = GlobalScope.launch(Dispatchers.IO) {
"--tunmtu", VPN_MTU.toString(), val localSocket = LocalSocket()
"--sock-path", "sock_path", localSocket.bind(
"--dnsgw", "127.0.0.1:${DataStore.portLocalDns}", LocalSocketAddress(
"--loglevel", "warning") protectPath,
if (profile.ipv6) { LocalSocketAddress.Namespace.FILESYSTEM
cmd += "--netif-ip6addr" )
cmd += PRIVATE_VLAN6_ROUTER )
} val socket = LocalServerSocket(localSocket.fileDescriptor)
cmd += "--enable-udprelay" val buffer = ByteBuffer.allocate(4)
data.processes!!.start(cmdSupplier = { cmd }, onRestartCallback = { while (true) {
try { val stream = socket.accept()
sendFd(conn.fileDescriptor) buffer.clear()
} catch (e: ErrnoException) { val n = stream.inputStream.read(buffer.array())
stopRunner(false, e.message) if (n == 4) {
val fd = buffer.int
if (!super.protect(fd)) {
println("protect failed")
}
buffer.clear()
buffer.putInt(0)
} else {
buffer.clear()
buffer.putInt(1)
}
stream.outputStream.write(buffer.array())
} }
}) }
return conn.fileDescriptor
}
private suspend fun sendFd(fd: FileDescriptor) { AccVpn.writeConfig(this, conn.fd, profile)
var tries = 0
val path = File(Core.deviceStorage.noBackupFilesDir, "sock_path").absolutePath thread(start = true) {
while (true) try { AccVpnService.instance.runAcc(
delay(50L shl tries) AccVpn.configFile(this).absolutePath,
LocalSocket().use { localSocket -> protectPath,
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM)) NetworkAdapter.JNI_CLAZZ_NAME,
localSocket.setFileDescriptorsForSend(arrayOf(fd)) VpnNativeDispatcher.JNI_CLAZZ_NAME,
localSocket.outputStream.write(42) )
}
return
} catch (e: IOException) {
if (tries > 5) throw e
tries += 1
} }
// val cmd = arrayListOf(File(applicationInfo.nativeLibraryDir, Executable.TUN2SOCKS).absolutePath,
// "--netif-ipaddr", PRIVATE_VLAN4_ROUTER,
// "--socks-server-addr", "${DataStore.listenAddress}:${DataStore.portProxy}",
// "--tunmtu", VPN_MTU.toString(),
// "--sock-path", "sock_path",
// "--dnsgw", "127.0.0.1:${DataStore.portLocalDns}",
// "--loglevel", "warning")
// if (profile.ipv6) {
// cmd += "--netif-ip6addr"
// cmd += PRIVATE_VLAN6_ROUTER
// }
// cmd += "--enable-udprelay"
// data.processes!!.start(cmdSupplier = { cmd }, onRestartCallback = {
// try {
// sendFd(conn.fileDescriptor)
// } catch (e: ErrnoException) {
// stopRunner(false, e.message)
// }
// })
return conn.fileDescriptor
} }
// private suspend fun sendFd(fd: FileDescriptor) {
// var tries = 0
// val path = File(Core.deviceStorage.noBackupFilesDir, "sock_path").absolutePath
// while (true) try {
// delay(50L shl tries)
// LocalSocket().use { localSocket ->
// localSocket.connect(
// LocalSocketAddress(
// path,
// LocalSocketAddress.Namespace.FILESYSTEM
// )
// )
// localSocket.setFileDescriptorsForSend(arrayOf(fd))
// localSocket.outputStream.write(42)
// }
// return
// } catch (e: IOException) {
// if (tries > 5) throw e
// tries += 1
// }
// }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
data.binder.close() data.binder.close()
......
...@@ -6,6 +6,9 @@ object HttpConfig { ...@@ -6,6 +6,9 @@ object HttpConfig {
//val baseUrl = "http://10.16.1.98:9002" //val baseUrl = "http://10.16.1.98:9002"
// val baseUrl = "https://test-cc-tt-api.orangenet.org.cn" //测试 // val baseUrl = "https://test-cc-tt-api.orangenet.org.cn" //测试
val baseUrl = "https://cc-tt-front.srccwl.com" //生产 val baseUrl = "https://cc-tt-front.srccwl.com" //生产
// val baseUrl = "http://192.168.137.25:9002"
// val baseUrl = "https://test-cc-tt-api.orangenet.org.cn" //测试
// val baseUrl = "https://cc-tt-front.srccwl.com" //生产
/** /**
......
...@@ -92,7 +92,8 @@ object DefaultNetworkListener { ...@@ -92,7 +92,8 @@ object DefaultNetworkListener {
// NB: this runs in ConnectivityThread, and this behavior cannot be changed until API 26 // NB: this runs in ConnectivityThread, and this behavior cannot be changed until API 26
private object Callback : ConnectivityManager.NetworkCallback() { private object Callback : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) = runBlocking { networkActor.send(NetworkMessage.Put(network)) } override fun onAvailable(network: Network) = runBlocking {
networkActor.send(NetworkMessage.Put(network)) }
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
// it's a good idea to refresh capabilities // it's a good idea to refresh capabilities
runBlocking { networkActor.send(NetworkMessage.Update(network)) } runBlocking { networkActor.send(NetworkMessage.Update(network)) }
......
...@@ -110,4 +110,25 @@ object DataStore : OnPreferenceDataStoreChangeListener { ...@@ -110,4 +110,25 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var outboundBindInterface: String var outboundBindInterface: String
get() = privateStore.getString(Key.outboundBindInterface) ?: "" get() = privateStore.getString(Key.outboundBindInterface) ?: ""
set(value) = privateStore.putString(Key.outboundBindInterface, value) set(value) = privateStore.putString(Key.outboundBindInterface, value)
var pingProxyServer: String
get() = privateStore.getString(Key.pingProxyServer) ?: ""
set(value) = privateStore.putString(Key.pingProxyServer, value)
var pingProxyPort: Int
get() = privateStore.getInt(Key.pingProxyPort) ?: 0
set(value) = privateStore.putInt(Key.pingProxyPort, value)
var pingIntervalSec: Int
get() = privateStore.getInt(Key.pingIntervalSec) ?: 3
set(value) = privateStore.putInt(Key.pingIntervalSec, value)
var pingTimeoutMillis: Int
get() = privateStore.getInt(Key.pingTimeoutMillis) ?: 200
set(value) = privateStore.putInt(Key.pingTimeoutMillis, value)
var pingAccHost: String
get() = privateStore.getString(Key.pingAccHost) ?: ""
set(value) = privateStore.putString(Key.pingAccHost, value)
var pingAccPort: Int
get() = privateStore.getInt(Key.pingAccPort) ?: 80
set(value) = privateStore.putInt(Key.pingAccPort, value)
var pingNetReachableHost: String
get() = privateStore.getString(Key.pingNetReachableHost) ?: "baidu.com"
set(value) = privateStore.putString(Key.pingNetReachableHost, value)
} }
...@@ -71,6 +71,14 @@ object Key { ...@@ -71,6 +71,14 @@ object Key {
const val assetUpdateTime = "assetUpdateTime" const val assetUpdateTime = "assetUpdateTime"
const val pingProxyServer = "pingProxyServer"
const val pingProxyPort = "pingProxyPort"
const val pingIntervalSec = "pingIntervalSec"
const val pingTimeoutMillis = "pingTimeoutMillis"
const val pingAccHost = "pingAccHost"
const val pingAccPort = "pingAccPort"
const val pingNetReachableHost = "pingNetReachableHost"
// TV specific values // TV specific values
const val controlStats = "control.stats" const val controlStats = "control.stats"
const val controlImport = "control.import" const val controlImport = "control.import"
......
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