Skip to content

Commit

Permalink
fix(vpn): support metered hint on Android 9+
Browse files Browse the repository at this point in the history
  • Loading branch information
CCG authored and CCG committed Dec 1, 2019
1 parent 3b496a1 commit 87a71cc
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 24 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ android {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.sdkVersion
versionCode 17
versionName "0.0.3.5"
versionName "0.0.3.6"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

javaCompileOptions {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package com.github.cgg.clasha

import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.text.TextUtils
import android.widget.EditText
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.SwitchPreference
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import com.github.cgg.clasha.bg.BaseService
import com.github.cgg.clasha.data.DataStore
import com.github.cgg.clasha.utils.Key
import com.github.cgg.clasha.utils.getGson
import com.github.cgg.clasha.utils.remove
import com.github.cgg.clasha.widget.EditTextDialog
import com.takisoft.preferencex.AutoSummaryEditTextPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
Expand Down Expand Up @@ -46,6 +49,11 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
val dnsMode = findPreference<SimpleMenuPreference>(Key.dnsMode)
val clashLoglevel = findPreference<SimpleMenuPreference>(Key.clashLoglevel)

val serviceModeValue = DataStore.serviceMode
val meteredPr = findPreference<SwitchPreference>(Key.metered)
meteredPr!!.apply {
if (Build.VERSION.SDK_INT >= 28) isEnabled = serviceModeValue == Key.modeVpn else remove()
}
/*val onServiceModeChange = Preference.OnPreferenceChangeListener { preference, newValue ->
val (enabledLocalDns, enabledTransproxy) = when (newValue as String?) {
Key.modeProxy -> Pair(false, false)
Expand All @@ -67,6 +75,7 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
portApi?.isEnabled = true
clashLoglevel?.isEnabled = true
dnsMode?.isEnabled = true
meteredPr?.isEnabled = true
} else {
serviceMode?.isEnabled = false
portHttpProxy?.isEnabled = false
Expand All @@ -77,6 +86,7 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
portApi?.isEnabled = false
clashLoglevel?.isEnabled = false
dnsMode?.isEnabled = false
meteredPr?.isEnabled = false
}
}

Expand Down
30 changes: 12 additions & 18 deletions app/src/main/java/com/github/cgg/clasha/bg/VpnService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,6 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
override fun getLocalizedMessage() = getString(R.string.reboot_required)
}

@TargetApi(Build.VERSION_CODES.P)
private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
underlyingNetwork = network
}

override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
// it's a good idea to refresh capabilities
underlyingNetwork = network
}

override fun onLost(network: Network) {
underlyingNetwork = null
}
}

private inner class ProtectWorker : ConcurrentLocalSocketListener(
"ClashVpnThread",
File(app.deviceStorage.noBackupFilesDir, "protect_path")
Expand Down Expand Up @@ -108,12 +92,16 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
private var conn: ParcelFileDescriptor? = null
private var worker: ProtectWorker? = null
private var active = false
private var metered = false
private var underlyingNetwork: Network? = null
set(value) {
field = value
if (active && Build.VERSION.SDK_INT >= 22) setUnderlyingNetworks(underlyingNetworks)
}
private val underlyingNetworks get() = underlyingNetwork?.let { arrayOf(it) }
private val underlyingNetworks
get() =
// clearing underlyingNetworks makes Android 9 consider the network to be metered
if (Build.VERSION.SDK_INT == 28 && metered) null else underlyingNetwork?.let { arrayOf(it) }

override fun onBind(intent: Intent) = when (intent.action) {
SERVICE_INTERFACE -> super<BaseVpnService>.onBind(intent)
Expand Down Expand Up @@ -177,9 +165,15 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
//bypass us
builder.addDisallowedApplication(packageName)
builder.addDisallowedApplication("$packageName:bg")

//todo APP bypass

metered = DataStore.metered
active = true
if (Build.VERSION.SDK_INT >= 22) {
builder.setUnderlyingNetworks(underlyingNetworks)
if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)
}

val conn = builder.establish() ?: throw NullConnectionException()
this.conn = conn
val fd = conn.fd
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/com/github/cgg/clasha/data/DataStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ object DataStore {
get() = publicStore.getString(Key.clashLoglevel, Key.clashLogInfo)!!
set(value) = publicStore.putString(Key.clashLoglevel, value)

var metered: Boolean
get() = publicStore.getBoolean(Key.metered, false)
set(value) = publicStore.putBoolean(Key.metered, value)
var portProxy: Int
get() = getLocalPort(Key.portProxy, 7891)
set(value) = publicStore.putString(Key.portProxy, value.toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ object DefaultNetworkListener {
check(listeners.isNotEmpty()) { "Getting network without any listeners is not supported" }
if (network == null) pendingRequests += message else message.response.complete(network)
}
is NetworkMessage.Stop -> if (!listeners.isEmpty() && // was not empty
listeners.remove(message.key) != null && listeners.isEmpty()) {
is NetworkMessage.Stop -> if (listeners.isNotEmpty() && // was not empty
listeners.remove(message.key) != null && listeners.isEmpty()) {
network = null
unregister()
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/github/cgg/clasha/utils/Key.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ object Key {
const val portHttpProxy = "portHttpProxy"
const val portLocalDns = "portLocalDns"
const val portTransproxy = "portTransproxy"

const val portApi = "portApi"
const val metered = "metered"

const val route = "route"

Expand Down
5 changes: 4 additions & 1 deletion app/src/main/java/com/github/cgg/clasha/utils/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import android.text.TextUtils
import android.util.Log
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.preference.Preference
import com.blankj.utilcode.util.LogUtils
import com.crashlytics.android.Crashlytics
import com.github.cgg.clasha.App
Expand Down Expand Up @@ -325,4 +326,6 @@ class JSONArrayAdapter : JsonSerializer<JSONArray>, JsonDeserializer<JSONArray>
}
}

}
}

fun Preference.remove() = parent!!.removePreference(this)
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/ic_device_data_usage.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorControlNormal">
<path android:fillColor="#FF000000" android:pathData="M13,2.05v3.03c3.39,0.49 6,3.39 6,6.92 0,0.9 -0.18,1.75 -0.48,2.54l2.6,1.53c0.56,-1.24 0.88,-2.62 0.88,-4.07 0,-5.18 -3.95,-9.45 -9,-9.95zM12,19c-3.87,0 -7,-3.13 -7,-7 0,-3.53 2.61,-6.43 6,-6.92V2.05c-5.06,0.5 -9,4.76 -9,9.95 0,5.52 4.47,10 9.99,10 3.31,0 6.24,-1.61 8.06,-4.09l-2.6,-1.53C16.17,17.98 14.21,19 12,19z"/>
</vector>
3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@
<string name="dns_custom_fallback">Dns Custom(fallback)</string>
<string name="allow_lan">Allow Lan</string>
<string name="ipv6Enable">IPV6 Enable</string>

<string name="metered">Metered Hint</string>
<string name="metered_summary">Hint system to treat VPN as metered</string>

<!-- alert category -->
<string name="profile_empty">Please select a profile</string>
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/xml/pref_global.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@
android:singleLine="true"
android:title="@string/port_proxy"/>

<SwitchPreference
android:key="metered"
android:icon="@drawable/ic_device_data_usage"
android:summary="@string/metered_summary"
android:title="@string/metered"/>

<AutoSummaryEditTextPreference
android:key="portLocalDns"
android:icon="@drawable/ic_action_dns"
Expand Down

0 comments on commit 87a71cc

Please sign in to comment.