3.18.4 virtual private network

This commit is contained in:
TenderIronh
2024-07-14 11:00:43 +08:00
parent e7b696c474
commit 9dda148232
62 changed files with 3846 additions and 899 deletions

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@@ -1,7 +1,11 @@
## Build
depends on openjdk 11, gradle 8.1.3, ndk 21
```
cd core
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init
go get -v golang.org/x/mobile/bind
cd core
gomobile bind -target android -v
if [[ $? -ne 0 ]]; then
echo "build error"

View File

@@ -1,62 +1,63 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
signingConfigs {
release {
storeFile file('C:\\work\\src\\openp2p-client\\app\\openp2p.jks')
storePassword 'YOUR-PASSWORD'
keyAlias 'openp2p.keys'
keyPassword 'YOUR-PASSWORD'
}
}
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cn.openp2p"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "2718281828"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation files('libs\\openp2p-sources.jar')
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
signingConfigs {
release {
storeFile file('C:\\work\\src\\openp2p-client\\app\\openp2p.jks')
storePassword 'YOUR-PASSWORD'
keyAlias 'openp2p.keys'
keyPassword 'YOUR-PASSWORD'
}
}
compileSdkVersion 31
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cn.openp2p"
minSdkVersion 16
targetSdkVersion 31
versionCode 1
versionName "2718281828"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
namespace "cn.openp2p"
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation files('libs\\openp2p-sources.jar')
}

View File

@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.openp2p">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
@@ -27,13 +26,21 @@
<activity
android:name=".ui.login.LoginActivity"
android:label="@string/app_name">
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="BootReceiver"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -0,0 +1,27 @@
package cn.openp2p
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.os.Build
import android.os.Bundle
import android.util.Log
import cn.openp2p.ui.login.LoginActivity
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Logger.log("pp onReceive "+intent.action.toString())
Log.i("onReceive","start "+intent.action.toString())
// if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
// Log.i("onReceive","match "+intent.action.toString())
// VpnService.prepare(context)
// val intent = Intent(context, OpenP2PService::class.java)
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// context.startForegroundService(intent)
// } else {
// context.startService(intent)
// }
// }
Log.i("onReceive","end "+intent.action.toString())
}
}

View File

@@ -4,9 +4,12 @@ import android.app.*
import android.content.Context
import android.content.Intent
import android.graphics.Color
import java.io.IOException
import android.net.VpnService
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
@@ -14,9 +17,22 @@ import cn.openp2p.ui.login.LoginActivity
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import openp2p.Openp2p
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.ByteBuffer
import kotlinx.coroutines.*
import org.json.JSONObject
data class Node(val name: String, val ip: String, val resource: String? = null)
class OpenP2PService : Service() {
data class Network(
val id: Long,
val name: String,
val gateway: String,
val Nodes: List<Node>
)
class OpenP2PService : VpnService() {
companion object {
private val LOG_TAG = OpenP2PService::class.simpleName
}
@@ -29,10 +45,12 @@ class OpenP2PService : Service() {
private lateinit var network: openp2p.P2PNetwork
private lateinit var mToken: String
private var running:Boolean =true
private var sdwanRunning:Boolean =false
private var vpnInterface: ParcelFileDescriptor? = null
private var sdwanJob: Job? = null
override fun onCreate() {
Log.i(LOG_TAG, "onCreate - Thread ID = " + Thread.currentThread().id)
var channelId: String? = null
channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel("kim.hsl", "ForegroundService")
} else {
""
@@ -41,7 +59,7 @@ class OpenP2PService : Service() {
val pendingIntent = PendingIntent.getActivity(
this, 0,
notificationIntent, 0
notificationIntent, PendingIntent.FLAG_IMMUTABLE
)
val notification = channelId?.let {
@@ -54,7 +72,7 @@ class OpenP2PService : Service() {
startForeground(1337, notification)
super.onCreate()
refreshSDWAN()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -62,23 +80,172 @@ class OpenP2PService : Service() {
LOG_TAG,
"onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().id
)
startOpenP2P(null)
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(p0: Intent?): IBinder? {
val token = p0?.getStringExtra("token")
Log.i(LOG_TAG, "onBind - Thread ID = " + Thread.currentThread().id + token)
Log.i(LOG_TAG, "onBind token=$token")
startOpenP2P(token)
return binder
}
private fun startOpenP2P(token : String?): Boolean {
if (sdwanRunning) {
return true
}
Log.i(LOG_TAG, "startOpenP2P - Thread ID = " + Thread.currentThread().id + token)
val oldToken = Openp2p.getToken(getExternalFilesDir(null).toString())
Log.i(LOG_TAG, "startOpenP2P oldtoken=$oldToken newtoken=$token")
if (oldToken=="0" && token==null){
return false
}
sdwanRunning = true
// runSDWAN()
GlobalScope.launch {
network = Openp2p.runAsModule(getExternalFilesDir(null).toString(), token, 0, 1)
network = Openp2p.runAsModule(
getExternalFilesDir(null).toString(),
token,
0,
1
) // /storage/emulated/0/Android/data/cn.openp2p/files/
val isConnect = network.connect(30000) // ms
Log.i(OpenP2PService.LOG_TAG, "login result: " + isConnect.toString());
Log.i(LOG_TAG, "login result: " + isConnect.toString());
do {
Thread.sleep(1000)
}while(network.connect(30000)&&running)
} while (network.connect(30000) && running)
stopSelf()
}
return binder
return false
}
private fun refreshSDWAN() {
GlobalScope.launch {
Log.i(OpenP2PService.LOG_TAG, "refreshSDWAN start");
while (true) {
Log.i(OpenP2PService.LOG_TAG, "waiting new sdwan config");
val buf = ByteArray(4096)
val buffLen = Openp2p.getAndroidSDWANConfig(buf)
Log.i(OpenP2PService.LOG_TAG, "closing running sdwan instance");
sdwanRunning = false
vpnInterface?.close()
vpnInterface = null
Thread.sleep(10000)
runSDWAN(buf.copyOfRange(0,buffLen.toInt() ))
}
Log.i(OpenP2PService.LOG_TAG, "refreshSDWAN end");
}
}
private suspend fun readTunLoop() {
val inputStream = FileInputStream(vpnInterface?.fileDescriptor).channel
if (inputStream==null){
Log.i(OpenP2PService.LOG_TAG, "open FileInputStream error: ");
return
}
Log.d(LOG_TAG, "read tun loop start")
val buffer = ByteBuffer.allocate(4096)
val byteArrayRead = ByteArray(4096)
while (sdwanRunning) {
buffer.clear()
val readBytes = inputStream.read(buffer)
if (readBytes <= 0) {
// Log.i(OpenP2PService.LOG_TAG, "inputStream.read error: ")
delay(1)
continue
}
buffer.flip()
buffer.get(byteArrayRead,0,readBytes)
Log.i(OpenP2PService.LOG_TAG, String.format("Openp2p.androidRead: %d", readBytes))
Openp2p.androidRead(byteArrayRead, readBytes.toLong())
}
Log.d(LOG_TAG, "read tun loop end")
}
private fun runSDWAN(buf:ByteArray) {
sdwanRunning=true
sdwanJob=GlobalScope.launch(context = Dispatchers.IO) {
Log.i(OpenP2PService.LOG_TAG, "runSDWAN start:${buf.decodeToString()}");
try{
var builder = Builder()
val jsonObject = JSONObject(buf.decodeToString())
val id = jsonObject.getLong("id")
val name = jsonObject.getString("name")
val gateway = jsonObject.getString("gateway")
val nodesArray = jsonObject.getJSONArray("Nodes")
val nodesList = mutableListOf<JSONObject>()
for (i in 0 until nodesArray.length()) {
nodesList.add(nodesArray.getJSONObject(i))
}
val myNodeName = Openp2p.getAndroidNodeName()
Log.i(OpenP2PService.LOG_TAG, "getAndroidNodeName:${myNodeName}");
val nodeList = nodesList.map {
val nodeName = it.getString("name")
val nodeIp = it.getString("ip")
if (nodeName==myNodeName){
builder.addAddress(nodeIp, 24)
}
val nodeResource = if (it.has("resource")) it.getString("resource") else null
val parts = nodeResource?.split("/")
if (parts?.size == 2) {
val ipAddress = parts[0]
val subnetMask = parts[1]
builder.addRoute(ipAddress, subnetMask.toInt())
Log.i(OpenP2PService.LOG_TAG, "sdwan addRoute:${ipAddress},${subnetMask.toInt()}");
}
Node(nodeName, nodeIp, nodeResource)
}
val network = Network(id, name, gateway, nodeList)
println(network)
Log.i(OpenP2PService.LOG_TAG, "onBind");
builder.addDnsServer("8.8.8.8")
builder.addRoute("10.2.3.0", 24)
// builder.addRoute("0.0.0.0", 0);
builder.setSession(LOG_TAG!!)
builder.setMtu(1420)
vpnInterface = builder.establish()
if (vpnInterface==null){
Log.e(OpenP2PService.LOG_TAG, "start vpnservice error: ");
}
val outputStream = FileOutputStream(vpnInterface?.fileDescriptor).channel
if (outputStream==null){
Log.e(OpenP2PService.LOG_TAG, "open FileOutputStream error: ");
return@launch
}
val byteArrayWrite = ByteArray(4096)
launch {
readTunLoop()
}
Log.d(LOG_TAG, "write tun loop start")
while (sdwanRunning) {
val len = Openp2p.androidWrite(byteArrayWrite)
Log.i(OpenP2PService.LOG_TAG, String.format("Openp2p.androidWrite: %d",len));
val writeBytes = outputStream?.write(ByteBuffer.wrap(byteArrayWrite))
if (writeBytes != null && writeBytes <= 0) {
Log.i(OpenP2PService.LOG_TAG, "outputStream?.write error: ");
continue
}
}
outputStream.close()
// 关闭 VPN 接口
vpnInterface?.close()
// 置空变量以释放资源
vpnInterface = null
Log.d(LOG_TAG, "write tun loop end")
}catch (e: Exception) {
// 捕获异常并记录
Log.e("VPN Connection", "发生异常: ${e.message}")
}
Log.i(OpenP2PService.LOG_TAG, "runSDWAN end");
}
}
override fun onDestroy() {
@@ -93,12 +260,13 @@ class OpenP2PService : Service() {
}
fun isConnected(): Boolean {
if (!::network.isInitialized) return false
return network?.connect(1000)
return network.connect(1000)
}
fun stop() {
running=false
stopSelf()
Openp2p.stop()
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String? {

View File

@@ -0,0 +1,45 @@
package cn.openp2p
import android.content.*
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.*
import android.app.*
import android.content.Context
import android.content.Intent
import android.graphics.Color
import java.io.IOException
import android.net.VpnService
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import cn.openp2p.ui.login.LoginActivity
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import openp2p.Openp2p
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.ByteBuffer
import kotlinx.coroutines.*
import org.json.JSONObject
object Logger {
private val logFile: File = File("app.log")
fun log(message: String) {
val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
val logMessage = "$timestamp: $message\n"
try {
val fileWriter = FileWriter(logFile, true)
fileWriter.append(logMessage)
fileWriter.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -5,6 +5,7 @@ import android.app.Activity
import android.app.ActivityManager
import android.app.Notification
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -25,6 +26,7 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import cn.openp2p.Logger
import cn.openp2p.OpenP2PService
import cn.openp2p.R
import cn.openp2p.databinding.ActivityLoginBinding
@@ -51,6 +53,14 @@ class LoginActivity : AppCompatActivity() {
private lateinit var loginViewModel: LoginViewModel
private lateinit var binding: ActivityLoginBinding
private lateinit var mService: OpenP2PService
@RequiresApi(Build.VERSION_CODES.O)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 0 && resultCode == Activity.RESULT_OK) {
startService(Intent(this, OpenP2PService::class.java))
}
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -78,22 +88,18 @@ class LoginActivity : AppCompatActivity() {
token.error = getString(loginState.passwordError)
}
})
val intent1 = VpnService.prepare(this) ?: return
loginViewModel.loginResult.observe(this@LoginActivity, Observer {
val loginResult = it ?: return@Observer
openp2pLog.setText(R.string.phone_setting)
val intent = VpnService.prepare(this)
if (intent != null)
{
Log.i("openp2p", "VpnService.prepare need permission");
startActivityForResult(intent, 0)
}
else {
Log.i("openp2p", "VpnService.prepare ready");
onActivityResult(0, Activity.RESULT_OK, null)
}
loading.visibility = View.GONE
if (loginResult.error != null) {
showLoginFailed(loginResult.error)
}
if (loginResult.success != null) {
updateUiWithUser(loginResult.success)
}
setResult(Activity.RESULT_OK)
//Complete and destroy login activity once successful
finish()
})
profile.setOnClickListener {
val url = "https://console.openp2p.cn/profile"
@@ -110,17 +116,23 @@ class LoginActivity : AppCompatActivity() {
}
openp2pLog.setText(R.string.phone_setting)
token.setText(Openp2p.getToken(getExternalFilesDir(null).toString()))
login.setOnClickListener {
if (login.text.toString()=="退出"){
// val intent = Intent(this@LoginActivity, OpenP2PService::class.java)
// stopService(intent)
// val intent = Intent(this@LoginActivity, OpenP2PService::class.java)
// stopService(intent)
Log.i(LOG_TAG, "quit")
mService.stop()
unbindService(connection)
val intent = Intent(this@LoginActivity, OpenP2PService::class.java)
stopService(intent)
// 解绑服务
unbindService(connection)
// 结束当前 Activity
finish() // 或者使用 finishAffinity() 来结束整个应用程序
exitAPP()
// finishAffinity()
}
login.setText("退出")
@@ -139,13 +151,20 @@ class LoginActivity : AppCompatActivity() {
if (isConnect) {
onlineState.setText("在线")
} else {
onlineState.setText("离线")
onlineState.setText("正在登录")
}
}
} while (true)
}
}
val tokenText = Openp2p.getToken(getExternalFilesDir(null).toString())
token.setText(tokenText.toString())
// Check token length and automatically click login if length > 10
if (tokenText.length > 10) {
// Logger.log("performClick ")
login.performClick()
}
}
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)

View File

@@ -13,28 +13,27 @@
<EditText
android:id="@+id/token"
android:layout_width="225dp"
android:layout_height="46dp"
android:layout_width="250dp"
android:layout_height="45dp"
android:hint="Token"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionDone"
android:selectAllOnFocus="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="85dp"
android:layout_height="45dp"
android:layout_gravity="start"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:enabled="false"
android:text="@string/action_sign_in"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/token"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteY="-2dp" />
app:layout_constraintTop_toTopOf="parent" />
@@ -58,28 +57,36 @@
android:id="@+id/openp2pLog"
android:layout_width="359dp"
android:layout_height="548dp"
android:layout_marginTop="24dp"
android:ems="10"
android:inputType="none"
android:textIsSelectable="true"
android:focusable="false"
android:inputType="none"
android:text="Name"
android:textIsSelectable="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/onlineState" />
<Button
android:id="@+id/profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="250dp"
android:layout_height="45dp"
android:layout_marginTop="8dp"
android:text="打开控制台查看Token"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/token" />
<EditText
android:id="@+id/onlineState"
android:layout_width="113dp"
android:layout_width="85dp"
android:layout_height="45dp"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:ems="10"
android:inputType="textPersonName"
android:text="未登录"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/profile"
app:layout_constraintTop_toBottomOf="@+id/login" />

View File

@@ -10,7 +10,14 @@
<string name="invalid_password">Token可以在 https://console.openp2p.cn/profile 获得</string>
<string name="login_failed">"Login failed"</string>
<string name="phone_setting">"安卓系统默认设置的”杀后台进程“会导致 OpenP2P 在后台运行一会后,被系统杀死进程,导致您的体验受到影响。您可以通过以下方式修改几个设置,解决此问题:
华为鸿蒙:
1. 允许应用后台运行:进入设置 → 搜索进入 应用启动管理 → 关闭 OpenP2P 的 自动管理 开关 → 在弹框中勾选 允许后台活动
2. 避免应用被电池优化程序清理:进入设置 → 搜索进入电池优化 → 不允许 →选择所有应用 → 找到无法后台运行的应用 → 设置为不允许
3. 关闭省电模式:进入设置 → 电池 → 关闭 省电模式 开关
4. 保持设备网络连接:进入设置 → 电池 → 更多电池设置 → 开启 休眠时始终保持网络连接 开关。
5. 给后台运行的应用加锁:打开应用后 → 进入多任务界面 → 下拉选中的卡片进行加锁 → 然后点击清理图标清理其他不经常使用的应用
6. 设置开发人员选项中相关开关:进入设置 → 搜索进入 开发人员选项 → 找到 不保留活动 开关后关闭 → 并在 后台进程限制 选择 标准限制
华为手机:
进入”设置“,搜索并进入“电池优化“界面,选中 OpenP2P 程序,不允许系统对其进行电池优化;
进入”设置“,进入”应用管理“界面,选中 OpenP2P 程序,点击”耗电情况“,开启”允许后台活动“即可;

View File

@@ -1,12 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.5.21"
ext.kotlin_version = "1.8.20"
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.1"
classpath "com.android.tools.build:gradle:8.1.3"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -1,6 +1,6 @@
#Sat Oct 22 21:46:24 CST 2022
#Tue Dec 05 16:04:08 CST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists