Skip to content

Commit e793f8c

Browse files
feat(probing): isolate RealPing and support policy-group members
1 parent b3c23fd commit e793f8c

18 files changed

Lines changed: 594 additions & 226 deletions

MikuRay/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,7 +1539,7 @@
15391539
android:name=".service.CoreTestService"
15401540
android:exported="false"
15411541
android:foregroundServiceType="specialUse"
1542-
android:process=":RunSoLibV2RayDaemon">
1542+
android:process=":Probe">
15431543
<property
15441544
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
15451545
android:value="test" />
@@ -1559,7 +1559,7 @@
15591559
android:name=".service.SubscriptionUpdateService"
15601560
android:exported="false"
15611561
android:foregroundServiceType="specialUse"
1562-
android:process=":RunSoLibV2RayDaemon">
1562+
android:process=":SubscriptionUpdate">
15631563
<property
15641564
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
15651565
android:value="test" />

MikuRay/app/src/main/java/com/miku/ray/AngApplication.kt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.content.Context
66
import android.graphics.Typeface
77
import android.os.Bundle
88
import android.os.Build
9+
import android.os.Process
910
import androidx.work.Configuration
1011
import androidx.work.WorkManager
1112
import com.miku.ray.AppConfig
@@ -48,11 +49,30 @@ class AngApplication : Application(), Application.ActivityLifecycleCallbacks {
4849

4950
Timber.plant(MikuRayLogTree())
5051
MmkvManager.initialize(this)
51-
ForegroundActivityTracker.register(this)
52-
registerActivityLifecycleCallbacks(this)
53-
WorkManager.initialize(this, workManagerConfiguration)
5452
SettingsManager.initApp(this)
55-
SettingsManager.setNightMode()
53+
54+
// Probe and subscription-update services intentionally run in disposable
55+
// processes. UI lifecycle tracking and WorkManager belong to the main/
56+
// background process and can prevent those services from starting cleanly.
57+
if (!isDisposableProcess()) {
58+
ForegroundActivityTracker.register(this)
59+
registerActivityLifecycleCallbacks(this)
60+
WorkManager.initialize(this, workManagerConfiguration)
61+
SettingsManager.setNightMode()
62+
}
63+
}
64+
65+
private fun isDisposableProcess(): Boolean {
66+
val processName = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
67+
Application.getProcessName()
68+
} else {
69+
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as? android.app.ActivityManager
70+
activityManager?.runningAppProcesses
71+
?.firstOrNull { it.pid == Process.myPid() }
72+
?.processName
73+
.orEmpty()
74+
}
75+
return processName.endsWith(":Probe") || processName.endsWith(":SubscriptionUpdate")
5676
}
5777

5878
/**

MikuRay/app/src/main/java/com/miku/ray/core/CoreConfigContextBuilder.kt

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,29 @@ object CoreConfigContextBuilder {
116116
return resolvedOutbounds
117117
}
118118

119-
private fun resolvePolicyGroupProfiles(config: ProfileItem): List<ProfileItem> {
119+
internal fun resolvePolicyGroupGuids(config: ProfileItem): List<String> =
120+
resolvePolicyGroupMembers(config)?.map { it.first }?.distinct().orEmpty()
121+
122+
private fun resolvePolicyGroupProfiles(config: ProfileItem): List<ProfileItem> =
123+
resolvePolicyGroupMembers(config)?.map { it.second } ?: listOf(config)
124+
125+
private fun resolvePolicyGroupMembers(config: ProfileItem): List<Pair<String, ProfileItem>>? {
120126
try {
121127
val serverList = MmkvManager.decodeAllServerList()
122128
return serverList
123129
.asSequence()
124-
.mapNotNull { id -> MmkvManager.decodeServerConfig(id) }
125-
.filter { profile ->
130+
.mapNotNull { guid ->
131+
MmkvManager.decodeServerConfig(guid)?.let { profile -> guid to profile }
132+
}
133+
.filter { (_, profile) ->
126134
val subscriptionId = config.policyGroupSubscriptionId
127135
if (subscriptionId.isNullOrBlank()) {
128136
true
129137
} else {
130138
profile.subscriptionId == subscriptionId
131139
}
132140
}
133-
.filter { profile ->
141+
.filter { (_, profile) ->
134142
val filter = config.policyGroupFilter
135143
if (filter.isNullOrBlank()) {
136144
true
@@ -142,13 +150,15 @@ object CoreConfigContextBuilder {
142150
}
143151
}
144152
}
145-
.filter { it.server.isNotNullEmpty() }
146-
.filter { Utils.isPureIpAddress(it.server!!) || Utils.isValidUrl(it.server!!) }
147-
.filter { !it.configType.isComplexType() }
153+
.filter { (_, profile) -> profile.server.isNotNullEmpty() }
154+
.filter { (_, profile) ->
155+
Utils.isPureIpAddress(profile.server!!) || Utils.isValidUrl(profile.server!!)
156+
}
157+
.filter { (_, profile) -> !profile.configType.isComplexType() }
148158
.toList()
149159
} catch (e: Exception) {
150160
LogUtil.e(AppConfig.TAG, "Failed to resolve policy group profiles for '${config.remarks}'", e)
151-
return listOf(config)
161+
return null
152162
}
153163
}
154164

MikuRay/app/src/main/java/com/miku/ray/core/CoreConfigManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ object CoreConfigManager {
993993
balancerTag: String = AppConfig.TAG_BALANCER,
994994
fallbackTag: String? = null,
995995
): BalancerStrategy {
996-
val probeUrl = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL) ?: AppConfig.DELAY_TEST_URL
996+
val probeUrl = SettingsManager.getDelayTestUrl()
997997
val leastPingInterval = decodeObservatoryDuration(AppConfig.PREF_OBSERVATORY_LEAST_PING_INTERVAL, AppConfig.OBSERVATORY_LEAST_PING_INTERVAL)
998998
val leastLoadInterval = decodeObservatoryDuration(AppConfig.PREF_OBSERVATORY_LEAST_LOAD_INTERVAL, AppConfig.OBSERVATORY_LEAST_LOAD_INTERVAL)
999999
val leastLoadMethod = MmkvManager.decodeSettingsString(AppConfig.PREF_OBSERVATORY_LEAST_LOAD_METHOD, AppConfig.OBSERVATORY_LEAST_LOAD_METHOD)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.miku.ray.dto
2+
3+
import java.io.Serializable
4+
5+
/** One persisted delay-test result delivered from the probe process to the UI. */
6+
data class RealPingResult(
7+
val testId: String,
8+
val guid: String,
9+
val delayMillis: Long,
10+
) : Serializable
11+
12+
/** Completed profile count for one active delay-test batch. */
13+
data class RealPingProgress(
14+
val testId: String,
15+
val completed: Int,
16+
val total: Int,
17+
) : Serializable
18+
19+
/** Terminal result for one delay-test batch. */
20+
data class RealPingSummary(
21+
val testId: String,
22+
val live: Int,
23+
val total: Int,
24+
val cancelled: Boolean,
25+
val listChanged: Boolean = false,
26+
) : Serializable

MikuRay/app/src/main/java/com/miku/ray/dto/RealPingEvent.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ package com.miku.ray.dto
22

33
sealed class RealPingEvent {
44

5-
data class Progress(
6-
val text: String,
7-
val guid: String = "",
8-
val delayMillis: Long = -1L,
9-
val current: Int = 0,
10-
val total: Int = 0
11-
) : RealPingEvent()
5+
/** Periodic progress update while the batch is still running. */
6+
data class Progress(val completed: Int, val total: Int) : RealPingEvent()
127

8+
/** A single server result is available. */
139
data class Result(val guid: String, val delayMillis: Long) : RealPingEvent()
1410

15-
data class Finish(val status: String) : RealPingEvent()
11+
/** Terminal counts for the batch. */
12+
data class Finish(
13+
val live: Int,
14+
val completed: Int,
15+
val total: Int,
16+
) : RealPingEvent()
1617
}
17-

MikuRay/app/src/main/java/com/miku/ray/dto/TestServiceMessage.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import java.io.Serializable
44

55
data class TestServiceMessage(
66
val key: Int,
7+
val testId: String = "",
78
val subscriptionId: String = "",
89
val serverGuids: List<String> = emptyList(),
910
val onlyTcp: Boolean = false

MikuRay/app/src/main/java/com/miku/ray/handler/SettingsManager.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -356,12 +356,12 @@ object SettingsManager {
356356
}
357357

358358
fun getDelayTestUrl(second: Boolean = false): String {
359-
return if (second) {
360-
AppConfig.DELAY_TEST_URL2
361-
} else {
362-
MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL)
363-
?: AppConfig.DELAY_TEST_URL
364-
}
359+
if (second) return AppConfig.DELAY_TEST_URL2
360+
361+
return MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL)
362+
?.trim()
363+
?.takeIf { it.isNotEmpty() }
364+
?: AppConfig.DELAY_TEST_URL
365365
}
366366

367367
fun getRealPingConcurrency(): Int {

MikuRay/app/src/main/java/com/miku/ray/handler/SubscriptionUpdater.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import androidx.work.multiprocess.RemoteWorkManager
1212
import androidx.work.workDataOf
1313
import com.miku.ray.AngApplication
1414
import com.miku.ray.AppConfig
15+
import com.miku.ray.R
1516
import com.miku.ray.dto.SubscriptionUpdateMessage
17+
import com.miku.ray.enums.NotificationChannelType
18+
import com.miku.ray.helper.NotificationHelper
1619
import com.miku.ray.util.LogUtil
1720
import com.miku.ray.util.MessageUtil
1821
import java.util.concurrent.TimeUnit
@@ -145,6 +148,16 @@ object SubscriptionUpdater {
145148
return Result.success()
146149
}
147150

151+
val subItem = MmkvManager.decodeSubscription(subId)
152+
NotificationHelper.notify(
153+
channelType = NotificationChannelType.SUBSCRIPTION_UPDATE,
154+
context = applicationContext,
155+
title = applicationContext.getString(R.string.title_pref_auto_update_subscription),
156+
content = applicationContext.getString(
157+
R.string.subscription_update_background_start,
158+
) + if (subItem?.remarks.isNullOrBlank()) "" else ": ${subItem?.remarks}",
159+
)
160+
148161
updateLastUpdatedAndReschedule(applicationContext, subId)
149162

150163
MessageUtil.sendMsg2SubscriptionService(

0 commit comments

Comments
 (0)