Commit d27ca493 authored by liusheng's avatar liusheng

合并分支

parents d823de6c dd2abac2
......@@ -22,21 +22,13 @@ android {
}
}
// compileSdk 30
// defaultConfig {
// applicationId = "com.ccwangluo.accelerator"
// minSdk 21
// targetSdk 30
// versionCode = 1
// versionName = "1.0"
//
// testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
// javaCompileOptions {
// annotationProcessor(Options {)
// arguments = listOf( moduleName : project.getName() )
// }
// }
// manifestPlaceholders = listOf(qqappid = "你的qq appid")
// }
defaultConfig {
applicationId = "com.ccwangluo.accelerator"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders.put("XG_ACCESS_ID","1580009634")
manifestPlaceholders.put("XG_ACCESS_KEY","AIJKAIYR3WPS")
}
buildTypes {
named("release"){
......@@ -123,4 +115,5 @@ dependencies {
implementation("com.tencent.bugly:crashreport_upgrade:1.5.23")//其中latest.release指代最新版本号,也可以指定明确的版本号,例如1.2.0
implementation ("com.tencent.bugly:nativecrashreport:3.9.2")//其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.2.0
implementation("com.tencent.tpns:tpns:1.3.2.0-release")
}
\ No newline at end of file
......@@ -170,3 +170,9 @@
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
-keep class android.support.**{*;}
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep class com.tencent.android.tpush.** {*;}
-keep class com.tencent.tpns.baseapi.** {*;}
-keep class com.tencent.tpns.mqttchannel.** {*;}
-keep class com.tencent.tpns.dataacquisition.** {*;}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.ccwangluo.accelerator">
<!-- 【必须】 移动推送 TPNS SDK VIP版本所需权限 -->
<permission
android:name="com.ccwangluo.accelerator.permission.XGPUSH_RECEIVE"
android:protectionLevel="signature" />
<uses-permission android:name="com.ccwangluo.accelerator.permission.XGPUSH_RECEIVE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
......@@ -17,7 +23,6 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 支持A-GPS辅助定位 -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 用于 log 日志 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- 配置可查询包名, 配置测试游戏必须添加 添加 -->
......@@ -35,6 +40,7 @@
<package android:name="com.tencent.mm" />
<package android:name="com.goplaycn.googleinstall" />
<package android:name="io.kkzs" />
<package android:name="com.eg.android.AlipayGphone" />
</queries>
......@@ -69,6 +75,26 @@
android:name="TencentMapSDK"
android:value="KZFBZ-QJ26J-WAYFC-FIO5N-FE453-QTFTT" />
<meta-data
android:name="XG_SERVER_SUFFIX"
android:value="tpns.sh.tencent.com" />
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<receiver android:name=".receiver.MyXGPushBaseReceiver">
<intent-filter>
<!-- 接收消息透传 -->
<action android:name="com.tencent.android.xg.vip.action.PUSH_MESSAGE" />
<!-- 监听注册、反注册、设置/删除标签、通知被点击等处理结果 -->
<action android:name="com.tencent.android.xg.vip.action.FEEDBACK" />
</intent-filter>
</receiver>
<!-- 在自身应用的AndroidManifest.xml文件中添加如下结点,其中 xxx 为任意自定义名称: -->
<!-- 关闭与 TPNS 应用的联合保活功能,请配置 -->
<provider
android:name="com.tencent.android.tpush.XGPushProvider"
tools:replace="android:authorities"
android:authorities="com.ccwangluo.accelerator.ccwangluo.XGVIP_PUSH_AUTH"
android:exported="false" />
<activity
android:name=".wxapi.WXEntryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
......
......@@ -12,6 +12,7 @@ import com.hjq.toast.ToastUtils
import com.tencent.bugly.Bugly
import com.tencent.bugly.BuglyStrategy
import com.tencent.bugly.beta.Beta
import com.tencent.android.tpush.XGPushConfig
import com.tencent.map.geolocation.TencentLocationManager
import com.tencent.mmkv.MMKV
import com.xuexiang.xpage.PageConfig
......@@ -33,6 +34,7 @@ class App : Application(), androidx.work.Configuration.Provider by Core {
MultiDex.install(this)
Core.init(this, MainActivity::class)
MMKV.initialize(this)
XGPushConfig.enableDebug(this,true);
}
fun initBugly(){
......
......@@ -6,8 +6,6 @@ data class NetState(
var accNetDelay: Int,
var delay : Int,
//丢包率
var lossRate: String
......
......@@ -8,7 +8,7 @@ data class News(
val abbrContent: String?,
val avatar: String?,
val createdDate: String?,
val greatNum: Int?,
var greatNum: Int,
val id: Int,
val imgs: String?,
var isGreat: Boolean,
......
package com.ccwangluo.accelerator.receiver
import android.content.Context
import com.tencent.android.tpush.*
class MyXGPushBaseReceiver : XGPushBaseReceiver (){
override fun onRegisterResult(p0: Context?, p1: Int, p2: XGPushRegisterResult?) {
}
override fun onUnregisterResult(p0: Context?, p1: Int) {
}
override fun onSetTagResult(p0: Context?, p1: Int, p2: String?) {
}
override fun onDeleteTagResult(p0: Context?, p1: Int, p2: String?) {
}
override fun onSetAccountResult(p0: Context?, p1: Int, p2: String?) {
}
override fun onDeleteAccountResult(p0: Context?, p1: Int, p2: String?) {
}
override fun onSetAttributeResult(p0: Context?, p1: Int, p2: String?) {
}
override fun onQueryTagsResult(p0: Context?, p1: Int, p2: String?, p3: String?) {
}
override fun onDeleteAttributeResult(p0: Context?, p1: Int, p2: String?) {
}
override fun onTextMessage(p0: Context?, p1: XGPushTextMessage?) {
}
override fun onNotificationClickedResult(p0: Context?, p1: XGPushClickedResult?) {
}
override fun onNotificationShowedResult(p0: Context?, p1: XGPushShowedResult?) {
}
}
\ No newline at end of file
......@@ -32,11 +32,18 @@ import com.xuexiang.xpage.core.PageOption
import android.hardware.SensorManager
import android.hardware.SensorEventListener
import android.os.Handler
import android.util.Log
import com.ccwangluo.accelerator.utils.SysUtils
import com.xuexiang.xutil.net.NetworkUtils
import com.xuexiang.xutil.system.PermissionUtils
import fm.jiecao.jcvideoplayer_lib.JCVideoPlayer
import java.security.Permission
import com.tencent.android.tpush.XGIOperateCallback
import com.tencent.android.tpush.XGPushManager
class MainActivity() : XPageActivity(), TencentLocationListener {
......@@ -113,6 +120,17 @@ class MainActivity() : XPageActivity(), TencentLocationListener {
private fun initPhoneUtils() {
PhoneUtils.getPhoneInfo()
//腾讯推送
XGPushManager.registerPush(this, object : XGIOperateCallback {
override fun onSuccess(data: Any, flag: Int) {
//token在设备卸载重装的时候有可能会变
Log.d("TPush", "注册成功,设备token为:$data")
}
override fun onFail(data: Any, errCode: Int, msg: String) {
Log.d("TPush", "注册失败,错误码:$errCode,错误信息:$msg")
}
})
}
override fun onBackPressed() {
......
......@@ -89,18 +89,18 @@ class AccelertorFragment : XPageFragment() {
binding.netLost2Unit.visibility = View.GONE
}
if (it.delay != -1) {
binding.netQuality.setText(SysUtils.getnetQuality(it.delay))
if (state == BaseService.State.Connected) {
binding.netQuality.setText(SysUtils.getnetQuality(it.accNetDelay))
} else {
binding.netQuality.setText(SysUtils.getnetQuality(it.preNetDelay))
}
binding.activeNetwork.setText(SysUtils.getActiveNet())
})
accelertorViewModel.accState.observe(this, {
binding.accStateTx.setText(SysUtils.getAccTimeString(acc_timer))
if (it != state) {
state = it
freshData(it)
}
state = it
freshData(it)
if (SettingSPUtils.isSmartAccOpen) {
binding.smartAccOpen.setText("已开启")
binding.smartAccOpen.setTextColor(resources.getColor(R.color.open_acc_enable))
......@@ -135,10 +135,10 @@ class AccelertorFragment : XPageFragment() {
binding.btnQuick.isEnabled = true
binding.progressViewCircleSmall.progress = 0F
binding.progressViewCircleSmall.postDelayed({
if(state != BaseService.State.Connected){
ToastUtils.show("加速失败")
}
},3000)
if (state != BaseService.State.Connected) {
ToastUtils.show("加速失败")
}
}, 3000)
AcceleratorUtils.startAccelerator(this@AccelertorFragment)
}
})
......@@ -293,12 +293,7 @@ class AccelertorFragment : XPageFragment() {
fun freshData(state: BaseService.State) {
if (state == BaseService.State.Connected) {
binding.gamePicIn1.startAnimation(getDefaultAnim())
binding.accStateTx.visibility = View.VISIBLE
binding.btnQuick.setBackgroundResource(R.mipmap.quick_stop)
if (binding.gamePicOut.background is AnimationDrawable) {
(binding.gamePicOut.background as AnimationDrawable).stop()
}
startAccSuccess()
if (SettingSPUtils.isFirstAccSuccess) {
context?.let {
CommonDialog(it).setTitle("防止加速掉线")
......@@ -310,7 +305,6 @@ class AccelertorFragment : XPageFragment() {
}
}
binding.gameStart.visibility = View.VISIBLE
} else {
binding.gamePicIn1.clearAnimation()
if (binding.gamePicOut.background is AnimationDrawable) {
......@@ -339,7 +333,18 @@ class AccelertorFragment : XPageFragment() {
fun startAnim() {
binding.progressViewCircleSmall.startProgressAnimation()
}
fun startAccSuccess(){
if (binding.gameStart.visibility == View.GONE) {
binding.gamePicIn1.startAnimation(getDefaultAnim())
binding.accStateTx.visibility = View.VISIBLE
binding.btnQuick.setBackgroundResource(R.mipmap.quick_stop)
if (binding.gamePicOut.background is AnimationDrawable) {
(binding.gamePicOut.background as AnimationDrawable).stop()
}
binding.gameStart.visibility = View.VISIBLE
}
}
fun getDefaultAnim(): RotateAnimation {
......
package com.ccwangluo.accelerator.ui.news
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
......@@ -25,6 +27,7 @@ import com.ccwangluo.accelerator.model.NewsModel
import com.ccwangluo.accelerator.ui.view.RatingBar
import com.ccwangluo.accelerator.ui.web.CommonWebViewFragment
import com.github.shadowsocks.http.HttpConfig
import com.hjq.toast.ToastUtils
class NewsObjectFragment : XPageFragment() {
......@@ -121,26 +124,32 @@ class NewsObjectFragment : XPageFragment() {
}
itemData.isGreat?.let {
if (it) {
itemView.findViewById<TextView>(R.id.great_num).setTextColor(Color.parseColor("#3CA4FD"))
itemView.findViewById<ImageView>(R.id.great_img)
.setBackgroundResource(R.mipmap.icon_praise_bu)
} else {
itemView.findViewById<TextView>(R.id.great_num).setTextColor(Color.parseColor("#FFBBBBBB"))
itemView.findViewById<ImageView>(R.id.great_img)
.setBackgroundResource(R.mipmap.icon_praise_ga)
}
}
itemView.findViewById<ImageView>(R.id.great_img).setOnClickListener {
itemData.isGreat?.let {
if (it) {
return@setOnClickListener
}
newsViewModel?.greate(itemData.type, gameId, itemData.id, {
itemData.isGreat = true
newsViewModel?.greate(this@NewsObjectFragment,itemData.type, gameId, itemData.id,it) {
if (!it) {
ToastUtils.show("点赞成功")
itemData.isGreat = true
itemData.greatNum++
} else {
itemData.isGreat = false
itemData.greatNum--
}
recycle_view?.adapter?.notifyDataSetChanged()
})
}
}
}
itemView.setOnClickListener {
itemView.findViewById<View>(R.id.item_click_view).setOnClickListener {
val params = Bundle()
params.putString(
CommonWebViewFragment.WEBVIEW_URL_KEY, HttpConfig.UI_MAIN_URL +
......
......@@ -4,7 +4,9 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ccwangluo.accelerator.model.NewsModel
import com.ccwangluo.accelerator.utils.LoginUtils
import com.ccwangluo.accelerator.utils.http.HttpGo
import com.xuexiang.xpage.base.XPageFragment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
......@@ -37,12 +39,17 @@ class NewsObjectModel : ViewModel() {
/**
* 点赞
*/
fun greate(type: Int?,parentId : Int, gameId: Int, callback: () -> Unit) {
val map = mutableMapOf<String, String>()
map.put("gameId", gameId.toString())
map.put("parentId",parentId.toString())
fun greate(xPageFragment: XPageFragment,type: Int?, gameId: Int, parentId: Int, isGreate: Boolean, callback: () -> Unit) {
val map = mutableMapOf<String, Any>()
map.put("gameId", gameId)
map.put("parentId", parentId)
map.put("isUnGreat", isGreate)
type?.let {
map.put("type", it.toString())
map.put("type", it)
}
if (LoginUtils.token == null) {
LoginUtils.login(xPageFragment,{})
return
}
viewModelScope.launch(Dispatchers.IO) {
val res =
......
......@@ -22,6 +22,7 @@ import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.http.LineConfig
import com.github.shadowsocks.net.NetworkManager
import com.github.shadowsocks.preference.DataStore
import com.hjq.toast.ToastUtils
import com.tencent.mmkv.MMKV
......@@ -53,12 +54,6 @@ object AcceleratorUtils {
//游戏
var game: GameInfo? = null
//加速前延迟
var pre_net = -1
//加速后延迟
var acc_net = -1
var stateListener: (BaseService.State, Long, NetState?) -> Unit = { state, l, net ->
}
......@@ -71,7 +66,7 @@ object AcceleratorUtils {
val res = HttpGo.getSync<Game>("/api/new/game/list?id=1&page=1&size=1")
res?.let {
game = it.data?.list?.get(0)
withContext(Dispatchers.Main){
withContext(Dispatchers.Main) {
callback(true)
}
game?.let {
......@@ -92,42 +87,37 @@ object AcceleratorUtils {
context.lifecycleScope.launch(Dispatchers.IO) {
while (true) {
game?.let {
val split = it.boosterServer.split(":")
var net = NetState(-1, -1, -1, "100")
net.delay = NetUtils.tcpPing(split[0], split[1].toInt()).toInt()
pre_net = net.delay
var net = NetState(-1, -1, "100")
if (state == BaseService.State.Connected) {
val delay = NetUtils.tcpPing(split[0], split[1].toInt(),DataStore.portProxy).toInt()
acc_net = (delay * 0.5).toInt()
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 * 0.5).toInt()
LoginUtils.getUserHasTimer {
if (!it) { //没有加速时长关闭服务
stopAccelerator()
}
}
net.delay = if (net.delay < delay) net.delay else delay
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 {
acc_net = -1
net.accNetDelay = -1
net.lossRate = if (net.preNetDelay == -1) "100" else "0"
}
net.lossRate = if (net.delay == -1) "100" else "0"
net.preNetDelay = pre_net
net.accNetDelay = acc_net
withContext(Dispatchers.Main) {
stateListener(state, getAccTime(), net)
}
delay(1000)
}
delay(2000)
}
}
context.lifecycleScope.launch(Dispatchers.IO) {
while (true) {
stateListener(state, getAccTime(), null)
delay(1000)
}
}
}
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
}
if (SysUtils.isVpn(context) && state != BaseService.State.Connected) {
......
......@@ -39,33 +39,6 @@ object SysUtils {
}
}
/**
* ping 网络状态
*/
suspend fun getNetPing(ip: String): NetState {
var delay: Int = -1
var lost = ""
val p = Runtime.getRuntime().exec("ping -c 4 ${ip}")
val buf = BufferedReader(InputStreamReader(p.inputStream))
var str: String? = String()
while (buf.readLine().also { str = it } != null) {
str?.let {
if (it.contains("packet loss")) {
val i = it.indexOf("received")
val j = it.indexOf("%")
// System.out.println("丢包率:"+str.substring(j-3, j+1));
lost = it.substring(i + 10, j)
}
if (it.contains("avg")) {
val i = it.indexOf("/", 20)
val j = it.indexOf(".", i)
delay = it.substring(i + 1, j).toInt()
}
}
}
return NetState(-1, -1, delay, lost)
}
/*
......
......@@ -7,6 +7,7 @@ import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.hjq.toast.ToastUtils
import com.xuexiang.xutil.XUtil
import com.umeng.vt.diff.V
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull
......@@ -82,7 +83,7 @@ object HttpGo {
/**
* 同步请求
*/
inline fun <reified T> postSync(url: String, param: MutableMap<String, String>): BaseModel<T>? {
inline fun <reified T> postSync(url: String, param: MutableMap<String, out Any>): BaseModel<T>? {
try {
val body: RequestBody = gson.toJson(param).toString().toRequestBody(jsonType)
val request = Request.Builder()
......
......@@ -159,13 +159,12 @@
android:id="@+id/cb_protocol"
android:padding="5dp"
android:gravity="center"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="25dp"
android:layout_height="25dp"
style="@style/btn_checkbox"
/>
<TextView
android:layout_marginLeft="5dp"
style="@style/TextStyle.Explain"
android:layout_width="wrap_content"
android:text="您已阅读并同意"
......
......@@ -21,7 +21,7 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40.5dp">
android:layout_marginTop="47.5dp">
<TextView
android:id="@+id/acc_state_tx"
......
......@@ -6,6 +6,13 @@
android:background="@color/white"
android:orientation="vertical">
<LinearLayout
android:id="@+id/item_click_view"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<LinearLayout
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
......@@ -109,7 +116,7 @@
android:layout_height="182.5dp"
android:layout_marginTop="12.65dp"
android:visibility="gone" />
</LinearLayout>
<RelativeLayout
android:layout_marginLeft="20dp"
......
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/xg_root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--通知的背景,id名字不能改变,其他可变-->
<ImageView
android:id="@+id/xg_notification_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<!--通知的大图标,id名字不能改变,其他可变.必须-->
<ImageView
android:id="@+id/xg_notification_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:scaleType="centerInside" />
<!--通知的时间,id名字不能改变,其他可变.若不显示时间可以去掉此布局-->
<TextView
android:id="@+id/xg_notification_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:textSize="12dp" />
<!--通知的标题,id名字不能改变,其他可变。必须-->
<TextView
android:id="@+id/xg_notification_style_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="20dp"
android:layout_toLeftOf="@id/xg_notification_date"
android:layout_toRightOf="@id/xg_notification_icon"
android:singleLine="true" />
<!--通知的内容,id名字不能改变,其他可变。必须-->
<TextView
android:id="@+id/xg_notification_style_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/xg_notification_style_title"
android:layout_alignLeft="@+id/xg_notification_style_title"
android:layout_marginTop="1dp"
android:layout_toLeftOf="@id/xg_notification_date"
android:singleLine="true" />
<!--带音频的富媒体通知的音频播放按钮,id名字不能改变,其他可变。若没用到音频富媒体可以去掉此布局-->
<ImageView
android:id="@+id/xg_notification_audio_play"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_alignLeft="@+id/xg_notification_style_title"
android:layout_alignParentBottom="true"
android:background="@android:drawable/ic_media_play"
android:visibility="gone" />
<!--带音频的富媒体通知的音频停止播放按钮,id名字不能改变,其他可变.若没用到音频富媒体可以去掉此布局-->
<ImageView
android:id="@+id/xg_notification_audio_stop"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="30dp"
android:layout_toRightOf="@+id/xg_notification_audio_play"
android:background="@android:drawable/ic_media_pause"
android:visibility="gone" />
</RelativeLayout>
\ No newline at end of file
......@@ -105,10 +105,6 @@
android:theme="@style/Theme.Shadowsocks.ActionBar"/>
<activity android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:theme="@style/Theme.Shadowsocks.ActionBar"/>
<service android:name="com.google.firebase.components.ComponentDiscoveryService"
android:directBootAware="true"/>
<provider android:name="com.google.firebase.provider.FirebaseInitProvider"
tools:node="remove"/>
<service android:name="androidx.room.MultiInstanceInvalidationService"
android:process=":bg"/>
</application>
......
......@@ -39,18 +39,23 @@ import kotlin.concurrent.thread
class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : CoroutineScope {
companion object {
private val pid by lazy {
Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid").apply { isAccessible = true }
Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid")
.apply { isAccessible = true }
}
}
private inner class Guard(private val cmd: List<String>) {
private inner class Guard(private val cmdSupplier: suspend () -> List<String>) {
private lateinit var process: Process
private lateinit var cmd: List<String>
private fun streamLogger(input: InputStream, logger: (String) -> Unit) = try {
input.bufferedReader().forEachLine(logger)
} catch (_: IOException) { } // ignore
} catch (_: IOException) {
} // ignore
fun start() {
suspend fun start() {
cmd = cmdSupplier.invoke()
Timber.i("start process: ${Commandline.toString(cmd)}")
process = ProcessBuilder(cmd).directory(Core.deviceStorage.noBackupFilesDir).start()
}
......@@ -74,7 +79,8 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
when {
exitCode == OsConstants.ECONNREFUSED -> throw IOException("connection refused by server")
SystemClock.elapsedRealtime() - startTime < 1000 -> throw IOException(
"$cmdName exits too fast (exit code: $exitCode)")
"$cmdName exits too fast (exit code: $exitCode)"
)
exitCode == 128 + OsConstants.SIGKILL -> Timber.w("$cmdName was killed")
else -> Timber.w(IOException("$cmdName unexpectedly exits with code $exitCode"))
}
......@@ -112,11 +118,15 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
override val coroutineContext = Dispatchers.Main.immediate + Job()
@MainThread
fun start(cmd: List<String>, onRestartCallback: (suspend () -> Unit)? = null) {
Timber.i("start process: ${Commandline.toString(cmd)}")
Guard(cmd).apply {
start() // if start fails, IOException will be thrown directly
launch { looper(onRestartCallback) }
fun start(
cmdSupplier: suspend () -> List<String>,
onRestartCallback: (suspend () -> Unit)? = null
) {
Guard(cmdSupplier).apply {
launch {
start() // if start fails, IOException will be thrown directly
looper(onRestartCallback)
}
}
}
......
......@@ -59,73 +59,80 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
* Sensitive shadowsocks configuration file requires extra protection. It may be stored in encrypted storage or
* device storage, depending on which is currently available.
*/
fun start(service: BaseService.Interface, stat: File, configFile: File, mode: String, dnsRelay: Boolean = true) {
suspend fun start(service: BaseService.Interface, stat: File, configFile: File, mode: String, dnsRelay: Boolean = true) {
// setup traffic monitor path
trafficMonitor = TrafficMonitor(stat)
// init JSON config
this.configFile = configFile
val config = profile.toJson()
plugin?.let { (path, opts, isV2) ->
if (service.isVpnService) {
if (isV2) opts["__android_vpn"] = "" else config.put("plugin_args", JSONArray(arrayOf("-V")))
val cmdSupplier: suspend () -> List<String> = {
AuthManager.initAuthProfile(profile)
// init JSON config
this.configFile = configFile
val config = profile.toJson()
plugin?.let { (path, opts, isV2) ->
if (service.isVpnService) {
if (isV2) opts["__android_vpn"] = "" else config.put("plugin_args", JSONArray(arrayOf("-V")))
}
config.put("plugin", path).put("plugin_opts", opts.toString())
}
config.put("plugin", path).put("plugin_opts", opts.toString())
}
config.put("dns", "unix://local_dns_path")
config.put("locals", JSONArray().apply {
// local SOCKS5 proxy
put(JSONObject().apply {
put("local_address", DataStore.listenAddress)
put("local_port", DataStore.portProxy)
put("local_udp_address", DataStore.listenAddress)
put("local_udp_port", DataStore.portProxy)
put("mode", mode)
})
// local DNS proxy
if (dnsRelay) try {
URI("dns://${profile.remoteDns}")
} catch (e: URISyntaxException) {
throw BaseService.ExpectedExceptionWrapper(e)
}.let { dns ->
config.put("dns", "unix://local_dns_path")
config.put("locals", JSONArray().apply {
// local SOCKS5 proxy
put(JSONObject().apply {
put("local_address", DataStore.listenAddress)
put("local_port", DataStore.portLocalDns)
put("local_dns_address", "local_dns_path")
put("remote_dns_address", dns.host ?: "0.0.0.0")
put("remote_dns_port", if (dns.port < 0) 53 else dns.port)
put("protocol", "dns")
put("mode", "udp_only")
put("local_port", DataStore.portProxy)
put("local_udp_address", DataStore.listenAddress)
put("local_udp_port", DataStore.portProxy)
put("mode", mode)
})
// local DNS proxy
if (dnsRelay) try {
URI("dns://${profile.remoteDns}")
} catch (e: URISyntaxException) {
throw BaseService.ExpectedExceptionWrapper(e)
}.let { dns ->
put(JSONObject().apply {
put("local_address", DataStore.listenAddress)
put("local_port", DataStore.portLocalDns)
put("local_dns_address", "local_dns_path")
put("remote_dns_address", dns.host ?: "0.0.0.0")
put("remote_dns_port", if (dns.port < 0) 53 else dns.port)
put("protocol", "dns")
put("mode", "udp_only")
})
}
})
if (DataStore.outboundBindInterface.isNotBlank()) {
config.put("outbound-bind-interface", DataStore.outboundBindInterface)
}
})
configFile.writeText(config.toString())
configFile.writeText(config.toString())
Timber.d("prepare start service with config: $config")
Timber.d("prepare start service with config: $config")
// build the command line
val cmd = arrayListOf(
// build the command line
val cmd = arrayListOf(
File((service as Context).applicationInfo.nativeLibraryDir, Executable.SS_LOCAL).absolutePath,
"--stat-path", stat.absolutePath,
"-c", configFile.absolutePath,
)
)
if (service.isVpnService) cmd += "--vpn"
if (service.isVpnService) cmd += "--vpn"
if (route != Acl.ALL) {
cmd += "--acl"
cmd += Acl.getFile(route).absolutePath
}
if (route != Acl.ALL) {
cmd += "--acl"
cmd += Acl.getFile(route).absolutePath
Timber.d("start vpn service with acl ${Acl.getFile(route).readText()}")
}
if (BuildConfig.DEBUG) {
cmd += "-v"
cmd += "-v"
cmd += "-v"
cmd += "-v"
if (BuildConfig.DEBUG) {
cmd += "-v"
cmd += "-v"
cmd += "-v"
cmd += "-v"
}
cmd
}
service.data.processes!!.start(cmd)
service.data.processes!!.start(cmdSupplier)
}
fun scheduleUpdate() {
......
......@@ -224,7 +224,7 @@ class VpnService : BaseVpnService(), BaseService.Interface {
cmd += PRIVATE_VLAN6_ROUTER
}
cmd += "--enable-udprelay"
data.processes!!.start(cmd, onRestartCallback = {
data.processes!!.start(cmdSupplier = { cmd }, onRestartCallback = {
try {
sendFd(conn.fileDescriptor)
} catch (e: ErrnoException) {
......
......@@ -107,4 +107,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var dirty: Boolean
get() = privateStore.getBoolean(Key.dirty) ?: false
set(value) = privateStore.putBoolean(Key.dirty, value)
var outboundBindInterface: String
get() = privateStore.getString(Key.outboundBindInterface) ?: ""
set(value) = privateStore.putString(Key.outboundBindInterface, value)
}
......@@ -65,6 +65,7 @@ object Key {
const val plugin = "plugin"
const val pluginConfigure = "plugin.configure"
const val udpFallback = "udpFallback"
const val outboundBindInterface = "outboundBindInterface"
const val dirty = "profileDirty"
......
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