Skip to content

Commit f9ff9f6

Browse files
authored
Merge pull request #2196 from shadowsocks/autoconnect+
Refine auto connecting
2 parents e7b672f + f6aa874 commit f9ff9f6

File tree

12 files changed

+63
-41
lines changed

12 files changed

+63
-41
lines changed

core/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
<intent-filter>
8181
<action android:name="android.intent.action.BOOT_COMPLETED"/>
8282
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
83+
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
8384
</intent-filter>
8485
</receiver>
8586

core/src/main/java/com/github/shadowsocks/BootReceiver.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import android.content.ComponentName
2525
import android.content.Context
2626
import android.content.Intent
2727
import android.content.pm.PackageManager
28+
import android.os.Build
29+
import android.os.UserManager
30+
import androidx.core.content.getSystemService
2831
import com.github.shadowsocks.Core.app
2932
import com.github.shadowsocks.preference.DataStore
3033

@@ -40,11 +43,16 @@ class BootReceiver : BroadcastReceiver() {
4043
}
4144

4245
override fun onReceive(context: Context, intent: Intent) {
43-
val locked = when (intent.action) {
44-
Intent.ACTION_BOOT_COMPLETED -> false
45-
Intent.ACTION_LOCKED_BOOT_COMPLETED -> true // constant will be folded so no need to do version checks
46-
else -> return
46+
if (!DataStore.persistAcrossReboot) { // sanity check
47+
enabled = false
48+
return
4749
}
48-
if (DataStore.directBootAware == locked) Core.startService()
50+
val doStart = when (intent.action) {
51+
Intent.ACTION_BOOT_COMPLETED -> !DataStore.directBootAware
52+
Intent.ACTION_LOCKED_BOOT_COMPLETED -> DataStore.directBootAware
53+
else -> DataStore.directBootAware ||
54+
Build.VERSION.SDK_INT >= 24 && app.getSystemService<UserManager>()?.isUserUnlocked != false
55+
}
56+
if (doStart) Core.startService()
4957
}
5058
}

core/src/main/java/com/github/shadowsocks/bg/BaseService.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ import android.util.Log
2929
import androidx.core.content.getSystemService
3030
import androidx.core.os.bundleOf
3131
import com.crashlytics.android.Crashlytics
32+
import com.github.shadowsocks.BootReceiver
3233
import com.github.shadowsocks.Core
3334
import com.github.shadowsocks.Core.app
3435
import com.github.shadowsocks.aidl.IShadowsocksService
3536
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
3637
import com.github.shadowsocks.aidl.TrafficStats
3738
import com.github.shadowsocks.core.R
3839
import com.github.shadowsocks.plugin.PluginManager
40+
import com.github.shadowsocks.preference.DataStore
3941
import com.github.shadowsocks.utils.Action
4042
import com.github.shadowsocks.utils.broadcastReceiver
4143
import com.github.shadowsocks.utils.printLog
@@ -76,6 +78,7 @@ object BaseService {
7678
var notification: ServiceNotification? = null
7779
val closeReceiver = broadcastReceiver { _, intent ->
7880
when (intent.action) {
81+
Intent.ACTION_SHUTDOWN -> service.persistStats()
7982
Action.RELOAD -> service.forceLoad()
8083
else -> service.stopRunner()
8184
}
@@ -284,10 +287,16 @@ object BaseService {
284287
data.changeState(State.Stopped, msg)
285288

286289
// stop the service if nothing has bound to it
287-
if (restart) startRunner() else stopSelf()
290+
if (restart) startRunner() else {
291+
BootReceiver.enabled = false
292+
stopSelf()
293+
}
288294
}
289295
}
290296

297+
fun persistStats() =
298+
listOfNotNull(data.proxy, data.udpFallback).forEach { it.trafficMonitor?.persistStats(it.profile.id) }
299+
291300
suspend fun preInit() { }
292301
suspend fun resolver(host: String) = InetAddress.getAllByName(host)
293302
suspend fun openConnection(url: URL) = url.openConnection()
@@ -309,6 +318,7 @@ object BaseService {
309318
data.proxy = proxy
310319
data.udpFallback = if (fallback == null) null else ProxyInstance(fallback, profile.route)
311320

321+
BootReceiver.enabled = DataStore.persistAcrossReboot
312322
if (!data.closeReceiverRegistered) {
313323
registerReceiver(data.closeReceiver, IntentFilter().apply {
314324
addAction(Action.RELOAD)

core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ import com.github.shadowsocks.Core
2828
import com.github.shadowsocks.acl.Acl
2929
import com.github.shadowsocks.acl.AclSyncer
3030
import com.github.shadowsocks.database.Profile
31-
import com.github.shadowsocks.database.ProfileManager
3231
import com.github.shadowsocks.plugin.PluginConfiguration
3332
import com.github.shadowsocks.plugin.PluginManager
3433
import com.github.shadowsocks.preference.DataStore
35-
import com.github.shadowsocks.utils.*
34+
import com.github.shadowsocks.utils.parseNumericAddress
35+
import com.github.shadowsocks.utils.signaturesCompat
36+
import com.github.shadowsocks.utils.useCancellable
3637
import kotlinx.coroutines.*
3738
import java.io.File
38-
import java.io.IOException
3939
import java.net.HttpURLConnection
4040
import java.net.URL
4141
import java.net.UnknownHostException
@@ -138,22 +138,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
138138
fun shutdown(scope: CoroutineScope) {
139139
trafficMonitor?.apply {
140140
thread.shutdown(scope)
141-
// Make sure update total traffic when stopping the runner
142-
try {
143-
// profile may have host, etc. modified and thus a re-fetch is necessary (possible race condition)
144-
val profile = ProfileManager.getProfile(profile.id) ?: return
145-
profile.tx += current.txTotal
146-
profile.rx += current.rxTotal
147-
ProfileManager.updateProfile(profile)
148-
} catch (e: IOException) {
149-
if (!DataStore.directBootAware) throw e // we should only reach here because we're in direct boot
150-
val profile = DirectBoot.getDeviceProfile()!!.toList().filterNotNull().single { it.id == profile.id }
151-
profile.tx += current.txTotal
152-
profile.rx += current.rxTotal
153-
profile.dirty = true
154-
DirectBoot.update(profile)
155-
DirectBoot.listenForUnlock()
156-
}
141+
persistStats(profile.id) // Make sure update total traffic when stopping the runner
157142
}
158143
trafficMonitor = null
159144
configFile?.delete() // remove old config possibly in device storage

core/src/main/java/com/github/shadowsocks/bg/TrafficMonitor.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ package com.github.shadowsocks.bg
2323
import android.net.LocalSocket
2424
import android.os.SystemClock
2525
import com.github.shadowsocks.aidl.TrafficStats
26+
import com.github.shadowsocks.database.ProfileManager
2627
import com.github.shadowsocks.net.LocalSocketListener
28+
import com.github.shadowsocks.preference.DataStore
29+
import com.github.shadowsocks.utils.DirectBoot
2730
import java.io.File
2831
import java.io.IOException
2932
import java.nio.ByteBuffer
@@ -52,6 +55,7 @@ class TrafficMonitor(statFile: File) {
5255
var out = TrafficStats()
5356
private var timestampLast = 0L
5457
private var dirty = false
58+
private var persisted = false
5559

5660
fun requestUpdate(): Pair<TrafficStats, Boolean> {
5761
val now = SystemClock.elapsedRealtime()
@@ -79,4 +83,24 @@ class TrafficMonitor(statFile: File) {
7983
}
8084
return Pair(out, updated)
8185
}
86+
87+
fun persistStats(id: Long) {
88+
check(!persisted) { "Double persisting?" }
89+
persisted = true
90+
try {
91+
// profile may have host, etc. modified and thus a re-fetch is necessary (possible race condition)
92+
val profile = ProfileManager.getProfile(id) ?: return
93+
profile.tx += current.txTotal
94+
profile.rx += current.rxTotal
95+
ProfileManager.updateProfile(profile)
96+
} catch (e: IOException) {
97+
if (!DataStore.directBootAware) throw e // we should only reach here because we're in direct boot
98+
val profile = DirectBoot.getDeviceProfile()!!.toList().filterNotNull().single { it.id == id }
99+
profile.tx += current.txTotal
100+
profile.rx += current.rxTotal
101+
profile.dirty = true
102+
DirectBoot.update(profile)
103+
DirectBoot.listenForUnlock()
104+
}
105+
}
82106
}

core/src/main/java/com/github/shadowsocks/preference/DataStore.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package com.github.shadowsocks.preference
2222

2323
import android.os.Binder
2424
import androidx.preference.PreferenceDataStore
25+
import com.github.shadowsocks.BootReceiver
2526
import com.github.shadowsocks.Core
2627
import com.github.shadowsocks.database.PrivateDatabase
2728
import com.github.shadowsocks.database.PublicDatabase
@@ -61,6 +62,8 @@ object DataStore : OnPreferenceDataStoreChangeListener {
6162
var profileId: Long
6263
get() = publicStore.getLong(Key.id) ?: 0
6364
set(value) = publicStore.putLong(Key.id, value)
65+
val persistAcrossReboot get() = publicStore.getBoolean(Key.persistAcrossReboot)
66+
?: BootReceiver.enabled.also { publicStore.putBoolean(Key.persistAcrossReboot, it) }
6467
val canToggleLocked: Boolean get() = publicStore.getBoolean(Key.directBootAware) == true
6568
val directBootAware: Boolean get() = Core.directBootSupported && canToggleLocked
6669
val tcpFastOpen: Boolean get() = TcpFastOpen.sendEnabled && DataStore.publicStore.getBoolean(Key.tfo, true)
@@ -101,6 +104,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
101104
* Initialize settings that have complicated default values.
102105
*/
103106
fun initGlobal() {
107+
persistAcrossReboot
104108
if (publicStore.getBoolean(Key.tfo) == null) publicStore.putBoolean(Key.tfo, tcpFastOpen)
105109
if (publicStore.getString(Key.portProxy) == null) portProxy = portProxy
106110
if (publicStore.getString(Key.portLocalDns) == null) portLocalDns = portLocalDns

core/src/main/java/com/github/shadowsocks/utils/Constants.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ object Key {
4343

4444
const val route = "route"
4545

46-
const val isAutoConnect = "isAutoConnect"
46+
const val persistAcrossReboot = "isAutoConnect"
4747
const val directBootAware = "directBootAware"
4848

4949
const val proxyApps = "isProxyApps"

core/src/main/res/values/strings.xml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@
5353
<string name="bypass_apps">Bypass</string>
5454
<string name="bypass_apps_summary">Enable this option to bypass selected apps</string>
5555
<string name="auto_connect">Auto Connect</string>
56-
<string name="auto_connect_summary">Enable Shadowsocks on startup</string>
57-
<string name="auto_connect_summary_v24">Enable Shadowsocks on startup. Recommended to use always-on VPN
58-
instead</string>
56+
<string name="auto_connect_summary">Enable Shadowsocks on startup/app update if it was running before</string>
5957
<string name="direct_boot_aware">Allow Toggling in Lock Screen</string>
6058
<string name="direct_boot_aware_summary">Your selected profile information will be less protected</string>
6159
<string name="tcp_fastopen_summary">Toggling might require ROOT permission</string>

mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,10 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
5050
preferenceManager.preferenceDataStore = DataStore.publicStore
5151
DataStore.initGlobal()
5252
addPreferencesFromResource(R.xml.pref_global)
53-
val boot = findPreference<SwitchPreference>(Key.isAutoConnect)!!
54-
boot.setOnPreferenceChangeListener { _, value ->
53+
findPreference<SwitchPreference>(Key.persistAcrossReboot)!!.setOnPreferenceChangeListener { _, value ->
5554
BootReceiver.enabled = value as Boolean
5655
true
5756
}
58-
boot.isChecked = BootReceiver.enabled
59-
if (Build.VERSION.SDK_INT >= 24) boot.setSummary(R.string.auto_connect_summary_v24)
6057

6158
val canToggleLocked = findPreference<Preference>(Key.directBootAware)!!
6259
if (Build.VERSION.SDK_INT >= 24) canToggleLocked.setOnPreferenceChangeListener { _, newValue ->

mobile/src/main/res/xml/pref_global.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
app:initialExpandedChildrenCount="4">
44
<SwitchPreference
55
app:key="isAutoConnect"
6-
app:persistent="false"
76
app:summary="@string/auto_connect_summary"
87
app:title="@string/auto_connect"/>
98
<SwitchPreference

0 commit comments

Comments
 (0)