Unverified Commit f9ff9f66 authored by Mygod's avatar Mygod Committed by GitHub

Merge pull request #2196 from shadowsocks/autoconnect+

Refine auto connecting
parents e7b672f9 f6aa874c
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
......
...@@ -25,6 +25,9 @@ import android.content.ComponentName ...@@ -25,6 +25,9 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.os.UserManager
import androidx.core.content.getSystemService
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
...@@ -40,11 +43,16 @@ class BootReceiver : BroadcastReceiver() { ...@@ -40,11 +43,16 @@ class BootReceiver : BroadcastReceiver() {
} }
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val locked = when (intent.action) { if (!DataStore.persistAcrossReboot) { // sanity check
Intent.ACTION_BOOT_COMPLETED -> false enabled = false
Intent.ACTION_LOCKED_BOOT_COMPLETED -> true // constant will be folded so no need to do version checks return
else -> return
} }
if (DataStore.directBootAware == locked) Core.startService() val doStart = when (intent.action) {
Intent.ACTION_BOOT_COMPLETED -> !DataStore.directBootAware
Intent.ACTION_LOCKED_BOOT_COMPLETED -> DataStore.directBootAware
else -> DataStore.directBootAware ||
Build.VERSION.SDK_INT >= 24 && app.getSystemService<UserManager>()?.isUserUnlocked != false
}
if (doStart) Core.startService()
} }
} }
...@@ -29,6 +29,7 @@ import android.util.Log ...@@ -29,6 +29,7 @@ import android.util.Log
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
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
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.aidl.IShadowsocksService
...@@ -36,6 +37,7 @@ import com.github.shadowsocks.aidl.IShadowsocksServiceCallback ...@@ -36,6 +37,7 @@ import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.aidl.TrafficStats import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.github.shadowsocks.plugin.PluginManager import com.github.shadowsocks.plugin.PluginManager
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Action import com.github.shadowsocks.utils.Action
import com.github.shadowsocks.utils.broadcastReceiver import com.github.shadowsocks.utils.broadcastReceiver
import com.github.shadowsocks.utils.printLog import com.github.shadowsocks.utils.printLog
...@@ -76,6 +78,7 @@ object BaseService { ...@@ -76,6 +78,7 @@ object BaseService {
var notification: ServiceNotification? = null var notification: ServiceNotification? = null
val closeReceiver = broadcastReceiver { _, intent -> val closeReceiver = broadcastReceiver { _, intent ->
when (intent.action) { when (intent.action) {
Intent.ACTION_SHUTDOWN -> service.persistStats()
Action.RELOAD -> service.forceLoad() Action.RELOAD -> service.forceLoad()
else -> service.stopRunner() else -> service.stopRunner()
} }
...@@ -284,10 +287,16 @@ object BaseService { ...@@ -284,10 +287,16 @@ object BaseService {
data.changeState(State.Stopped, msg) data.changeState(State.Stopped, msg)
// stop the service if nothing has bound to it // stop the service if nothing has bound to it
if (restart) startRunner() else stopSelf() if (restart) startRunner() else {
BootReceiver.enabled = false
stopSelf()
}
} }
} }
fun persistStats() =
listOfNotNull(data.proxy, data.udpFallback).forEach { it.trafficMonitor?.persistStats(it.profile.id) }
suspend fun preInit() { } suspend fun preInit() { }
suspend fun resolver(host: String) = InetAddress.getAllByName(host) suspend fun resolver(host: String) = InetAddress.getAllByName(host)
suspend fun openConnection(url: URL) = url.openConnection() suspend fun openConnection(url: URL) = url.openConnection()
...@@ -309,6 +318,7 @@ object BaseService { ...@@ -309,6 +318,7 @@ object BaseService {
data.proxy = proxy data.proxy = proxy
data.udpFallback = if (fallback == null) null else ProxyInstance(fallback, profile.route) data.udpFallback = if (fallback == null) null else ProxyInstance(fallback, profile.route)
BootReceiver.enabled = DataStore.persistAcrossReboot
if (!data.closeReceiverRegistered) { if (!data.closeReceiverRegistered) {
registerReceiver(data.closeReceiver, IntentFilter().apply { registerReceiver(data.closeReceiver, IntentFilter().apply {
addAction(Action.RELOAD) addAction(Action.RELOAD)
......
...@@ -28,14 +28,14 @@ import com.github.shadowsocks.Core ...@@ -28,14 +28,14 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.acl.AclSyncer import com.github.shadowsocks.acl.AclSyncer
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.plugin.PluginManager import com.github.shadowsocks.plugin.PluginManager
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.* import com.github.shadowsocks.utils.parseNumericAddress
import com.github.shadowsocks.utils.signaturesCompat
import com.github.shadowsocks.utils.useCancellable
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.io.IOException
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.net.UnknownHostException import java.net.UnknownHostException
...@@ -138,22 +138,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -138,22 +138,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
fun shutdown(scope: CoroutineScope) { fun shutdown(scope: CoroutineScope) {
trafficMonitor?.apply { trafficMonitor?.apply {
thread.shutdown(scope) thread.shutdown(scope)
// Make sure update total traffic when stopping the runner persistStats(profile.id) // Make sure update total traffic when stopping the runner
try {
// profile may have host, etc. modified and thus a re-fetch is necessary (possible race condition)
val profile = ProfileManager.getProfile(profile.id) ?: return
profile.tx += current.txTotal
profile.rx += current.rxTotal
ProfileManager.updateProfile(profile)
} catch (e: IOException) {
if (!DataStore.directBootAware) throw e // we should only reach here because we're in direct boot
val profile = DirectBoot.getDeviceProfile()!!.toList().filterNotNull().single { it.id == profile.id }
profile.tx += current.txTotal
profile.rx += current.rxTotal
profile.dirty = true
DirectBoot.update(profile)
DirectBoot.listenForUnlock()
}
} }
trafficMonitor = null trafficMonitor = null
configFile?.delete() // remove old config possibly in device storage configFile?.delete() // remove old config possibly in device storage
......
...@@ -23,7 +23,10 @@ package com.github.shadowsocks.bg ...@@ -23,7 +23,10 @@ package com.github.shadowsocks.bg
import android.net.LocalSocket import android.net.LocalSocket
import android.os.SystemClock import android.os.SystemClock
import com.github.shadowsocks.aidl.TrafficStats import com.github.shadowsocks.aidl.TrafficStats
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.utils.DirectBoot
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.ByteBuffer import java.nio.ByteBuffer
...@@ -52,6 +55,7 @@ class TrafficMonitor(statFile: File) { ...@@ -52,6 +55,7 @@ class TrafficMonitor(statFile: File) {
var out = TrafficStats() var out = TrafficStats()
private var timestampLast = 0L private var timestampLast = 0L
private var dirty = false private var dirty = false
private var persisted = false
fun requestUpdate(): Pair<TrafficStats, Boolean> { fun requestUpdate(): Pair<TrafficStats, Boolean> {
val now = SystemClock.elapsedRealtime() val now = SystemClock.elapsedRealtime()
...@@ -79,4 +83,24 @@ class TrafficMonitor(statFile: File) { ...@@ -79,4 +83,24 @@ class TrafficMonitor(statFile: File) {
} }
return Pair(out, updated) return Pair(out, updated)
} }
fun persistStats(id: Long) {
check(!persisted) { "Double persisting?" }
persisted = true
try {
// profile may have host, etc. modified and thus a re-fetch is necessary (possible race condition)
val profile = ProfileManager.getProfile(id) ?: return
profile.tx += current.txTotal
profile.rx += current.rxTotal
ProfileManager.updateProfile(profile)
} catch (e: IOException) {
if (!DataStore.directBootAware) throw e // we should only reach here because we're in direct boot
val profile = DirectBoot.getDeviceProfile()!!.toList().filterNotNull().single { it.id == id }
profile.tx += current.txTotal
profile.rx += current.rxTotal
profile.dirty = true
DirectBoot.update(profile)
DirectBoot.listenForUnlock()
}
}
} }
...@@ -22,6 +22,7 @@ package com.github.shadowsocks.preference ...@@ -22,6 +22,7 @@ package com.github.shadowsocks.preference
import android.os.Binder import android.os.Binder
import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceDataStore
import com.github.shadowsocks.BootReceiver
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.database.PrivateDatabase import com.github.shadowsocks.database.PrivateDatabase
import com.github.shadowsocks.database.PublicDatabase import com.github.shadowsocks.database.PublicDatabase
...@@ -61,6 +62,8 @@ object DataStore : OnPreferenceDataStoreChangeListener { ...@@ -61,6 +62,8 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var profileId: Long var profileId: Long
get() = publicStore.getLong(Key.id) ?: 0 get() = publicStore.getLong(Key.id) ?: 0
set(value) = publicStore.putLong(Key.id, value) set(value) = publicStore.putLong(Key.id, value)
val persistAcrossReboot get() = publicStore.getBoolean(Key.persistAcrossReboot)
?: BootReceiver.enabled.also { publicStore.putBoolean(Key.persistAcrossReboot, it) }
val canToggleLocked: Boolean get() = publicStore.getBoolean(Key.directBootAware) == true val canToggleLocked: Boolean get() = publicStore.getBoolean(Key.directBootAware) == true
val directBootAware: Boolean get() = Core.directBootSupported && canToggleLocked val directBootAware: Boolean get() = Core.directBootSupported && canToggleLocked
val tcpFastOpen: Boolean get() = TcpFastOpen.sendEnabled && DataStore.publicStore.getBoolean(Key.tfo, true) val tcpFastOpen: Boolean get() = TcpFastOpen.sendEnabled && DataStore.publicStore.getBoolean(Key.tfo, true)
...@@ -101,6 +104,7 @@ object DataStore : OnPreferenceDataStoreChangeListener { ...@@ -101,6 +104,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
* Initialize settings that have complicated default values. * Initialize settings that have complicated default values.
*/ */
fun initGlobal() { fun initGlobal() {
persistAcrossReboot
if (publicStore.getBoolean(Key.tfo) == null) publicStore.putBoolean(Key.tfo, tcpFastOpen) if (publicStore.getBoolean(Key.tfo) == null) publicStore.putBoolean(Key.tfo, tcpFastOpen)
if (publicStore.getString(Key.portProxy) == null) portProxy = portProxy if (publicStore.getString(Key.portProxy) == null) portProxy = portProxy
if (publicStore.getString(Key.portLocalDns) == null) portLocalDns = portLocalDns if (publicStore.getString(Key.portLocalDns) == null) portLocalDns = portLocalDns
......
...@@ -43,7 +43,7 @@ object Key { ...@@ -43,7 +43,7 @@ object Key {
const val route = "route" const val route = "route"
const val isAutoConnect = "isAutoConnect" const val persistAcrossReboot = "isAutoConnect"
const val directBootAware = "directBootAware" const val directBootAware = "directBootAware"
const val proxyApps = "isProxyApps" const val proxyApps = "isProxyApps"
......
...@@ -53,9 +53,7 @@ ...@@ -53,9 +53,7 @@
<string name="bypass_apps">Bypass</string> <string name="bypass_apps">Bypass</string>
<string name="bypass_apps_summary">Enable this option to bypass selected apps</string> <string name="bypass_apps_summary">Enable this option to bypass selected apps</string>
<string name="auto_connect">Auto Connect</string> <string name="auto_connect">Auto Connect</string>
<string name="auto_connect_summary">Enable Shadowsocks on startup</string> <string name="auto_connect_summary">Enable Shadowsocks on startup/app update if it was running before</string>
<string name="auto_connect_summary_v24">Enable Shadowsocks on startup. Recommended to use always-on VPN
instead</string>
<string name="direct_boot_aware">Allow Toggling in Lock Screen</string> <string name="direct_boot_aware">Allow Toggling in Lock Screen</string>
<string name="direct_boot_aware_summary">Your selected profile information will be less protected</string> <string name="direct_boot_aware_summary">Your selected profile information will be less protected</string>
<string name="tcp_fastopen_summary">Toggling might require ROOT permission</string> <string name="tcp_fastopen_summary">Toggling might require ROOT permission</string>
......
...@@ -50,13 +50,10 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { ...@@ -50,13 +50,10 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
preferenceManager.preferenceDataStore = DataStore.publicStore preferenceManager.preferenceDataStore = DataStore.publicStore
DataStore.initGlobal() DataStore.initGlobal()
addPreferencesFromResource(R.xml.pref_global) addPreferencesFromResource(R.xml.pref_global)
val boot = findPreference<SwitchPreference>(Key.isAutoConnect)!! findPreference<SwitchPreference>(Key.persistAcrossReboot)!!.setOnPreferenceChangeListener { _, value ->
boot.setOnPreferenceChangeListener { _, value ->
BootReceiver.enabled = value as Boolean BootReceiver.enabled = value as Boolean
true true
} }
boot.isChecked = BootReceiver.enabled
if (Build.VERSION.SDK_INT >= 24) boot.setSummary(R.string.auto_connect_summary_v24)
val canToggleLocked = findPreference<Preference>(Key.directBootAware)!! val canToggleLocked = findPreference<Preference>(Key.directBootAware)!!
if (Build.VERSION.SDK_INT >= 24) canToggleLocked.setOnPreferenceChangeListener { _, newValue -> if (Build.VERSION.SDK_INT >= 24) canToggleLocked.setOnPreferenceChangeListener { _, newValue ->
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
app:initialExpandedChildrenCount="4"> app:initialExpandedChildrenCount="4">
<SwitchPreference <SwitchPreference
app:key="isAutoConnect" app:key="isAutoConnect"
app:persistent="false"
app:summary="@string/auto_connect_summary" app:summary="@string/auto_connect_summary"
app:title="@string/auto_connect"/> app:title="@string/auto_connect"/>
<SwitchPreference <SwitchPreference
......
...@@ -157,12 +157,9 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -157,12 +157,9 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
stats = findPreference(Key.controlStats)!! stats = findPreference(Key.controlStats)!!
controlImport = findPreference(Key.controlImport)!! controlImport = findPreference(Key.controlImport)!!
findPreference<SwitchPreference>(Key.isAutoConnect)!!.apply { findPreference<SwitchPreference>(Key.persistAcrossReboot)!!.setOnPreferenceChangeListener { _, value ->
setOnPreferenceChangeListener { _, value -> BootReceiver.enabled = value as Boolean
BootReceiver.enabled = value as Boolean true
true
}
isChecked = BootReceiver.enabled
} }
tfo = findPreference(Key.tfo)!! tfo = findPreference(Key.tfo)!!
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
app:initialExpandedChildrenCount="3"> app:initialExpandedChildrenCount="3">
<SwitchPreference <SwitchPreference
app:key="isAutoConnect" app:key="isAutoConnect"
app:persistent="false"
app:summary="@string/auto_connect_summary" app:summary="@string/auto_connect_summary"
app:title="@string/auto_connect"/> app:title="@string/auto_connect"/>
<SwitchPreference <SwitchPreference
......
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