Support new Wi-Fi locks in Android Q

This commit is contained in:
Mygod
2019-03-20 11:42:05 +08:00
parent da9d647339
commit 841ee5ce9d
7 changed files with 82 additions and 9 deletions

View File

@@ -65,8 +65,20 @@ Default settings are picked to suit general use cases and maximize compatibility
* Keep Wi-Fi alive: Acquire Wi-Fi locks when repeater, temporary hotspot or system VPN hotspot is activated. * Keep Wi-Fi alive: Acquire Wi-Fi locks when repeater, temporary hotspot or system VPN hotspot is activated.
- Choose "System default" to save battery life; - Choose "System default" to save battery life;
- Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while; - (up to Android 9) Choose "On" (default) if repeater/hotspot turns itself off automatically or stops working after a while;
- Choose "High Performance Mode" to minimize packet loss and latency (will consume more power). - (up to Android 9) Choose "High Performance Mode" to minimize packet loss and latency (will consume more power);
- (since Android Q) Choose "Disable power save" to decrease packet latency.
An example use case is when a voice connection needs to be kept active even after the device screen goes off.
Using this mode may improve the call quality.
Requires support from the hardware.
- (since Android Q) Choose "Low latency mode" to optimize for reduced packet latency, and this might result in:
1. Reduced battery life.
2. Reduced throughput.
3. Reduced frequency of Wi-Fi scanning.
This may cause the device not roaming or switching to the AP with highest signal quality, and location accuracy may be reduced.
Example use cases are real time gaming or virtual reality applications where low latency is a key factor for user experience.
Requires support from the hardware.
Note: Requires this app running in foreground with screen on.
* Start repeater on boot: Self explanatory. * Start repeater on boot: Self explanatory.
* Network status monitor mode: This option controls how the app monitors connected devices as well as interface changes * Network status monitor mode: This option controls how the app monitors connected devices as well as interface changes
(when custom upstream is used). (when custom upstream is used).

View File

@@ -15,6 +15,7 @@ import be.mygod.vpnhotspot.client.ClientViewModel
import be.mygod.vpnhotspot.client.ClientsFragment import be.mygod.vpnhotspot.client.ClientsFragment
import be.mygod.vpnhotspot.databinding.ActivityMainBinding import be.mygod.vpnhotspot.databinding.ActivityMainBinding
import be.mygod.vpnhotspot.manage.TetheringFragment import be.mygod.vpnhotspot.manage.TetheringFragment
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.google.android.material.bottomnavigation.BottomNavigationMenuView import com.google.android.material.bottomnavigation.BottomNavigationMenuView
@@ -41,6 +42,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class) if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class)
model.clients.observe(this, Observer { badge.badgeNumber = it.size }) model.clients.observe(this, Observer { badge.badgeNumber = it.size })
SmartSnackbar.Register(lifecycle, binding.fragmentHolder) SmartSnackbar.Register(lifecycle, binding.fragmentHolder)
WifiDoubleLock.ActivityListener(this)
} }
override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) { override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) {

View File

@@ -12,6 +12,7 @@ import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.net.Routing.Companion.IPTABLES import be.mygod.vpnhotspot.net.Routing.Companion.IPTABLES
import be.mygod.vpnhotspot.net.monitor.IpMonitor import be.mygod.vpnhotspot.net.monitor.IpMonitor
import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
import be.mygod.vpnhotspot.preference.AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat import be.mygod.vpnhotspot.preference.AlwaysAutoCompleteEditTextPreferenceDialogFragmentCompat
import be.mygod.vpnhotspot.preference.SharedPreferenceDataStore import be.mygod.vpnhotspot.preference.SharedPreferenceDataStore
import be.mygod.vpnhotspot.util.RootSession import be.mygod.vpnhotspot.util.RootSession
@@ -26,6 +27,7 @@ import java.net.SocketException
class SettingsPreferenceFragment : PreferenceFragmentCompat() { class SettingsPreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
WifiDoubleLock.mode = WifiDoubleLock.mode // handle complicated default value and possible system upgrades
preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref) preferenceManager.preferenceDataStore = SharedPreferenceDataStore(app.pref)
RoutingManager.masqueradeMode = RoutingManager.masqueradeMode // flush default value RoutingManager.masqueradeMode = RoutingManager.masqueradeMode // flush default value
addPreferencesFromResource(R.xml.pref_settings) addPreferencesFromResource(R.xml.pref_settings)

View File

@@ -4,7 +4,15 @@ import android.annotation.SuppressLint
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import android.os.PowerManager import android.os.PowerManager
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.annotation.RequiresApi
import androidx.core.content.edit
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.os.BuildCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
/** /**
@@ -13,8 +21,12 @@ import be.mygod.vpnhotspot.App.Companion.app
class WifiDoubleLock(lockType: Int) : AutoCloseable { class WifiDoubleLock(lockType: Int) : AutoCloseable {
companion object : SharedPreferences.OnSharedPreferenceChangeListener { companion object : SharedPreferences.OnSharedPreferenceChangeListener {
private const val KEY = "service.wifiLock" private const val KEY = "service.wifiLock"
private val lockType get() = var mode: Mode
WifiDoubleLock.Mode.valueOf(app.pref.getString(KEY, WifiDoubleLock.Mode.Full.toString()) ?: "").lockType @Suppress("DEPRECATION")
get() = Mode.valueOf(app.pref.getString(KEY, Mode.Full.toString()) ?: "").let {
if (it == Mode.Full && BuildCompat.isAtLeastQ()) Mode.None else it
}
set(value) = app.pref.edit { putString(KEY, value.toString()) }
private val service by lazy { app.getSystemService<PowerManager>()!! } private val service by lazy { app.getSystemService<PowerManager>()!! }
private var holders = mutableSetOf<Any>() private var holders = mutableSetOf<Any>()
@@ -23,7 +35,7 @@ class WifiDoubleLock(lockType: Int) : AutoCloseable {
fun acquire(holder: Any) = synchronized(this) { fun acquire(holder: Any) = synchronized(this) {
if (holders.isEmpty()) { if (holders.isEmpty()) {
app.pref.registerOnSharedPreferenceChangeListener(this) app.pref.registerOnSharedPreferenceChangeListener(this)
val lockType = lockType val lockType = mode.lockType
if (lockType != null) lock = WifiDoubleLock(lockType) if (lockType != null) lock = WifiDoubleLock(lockType)
} }
check(holders.add(holder)) check(holders.add(holder))
@@ -40,14 +52,45 @@ class WifiDoubleLock(lockType: Int) : AutoCloseable {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == KEY) synchronized(this) { if (key == KEY) synchronized(this) {
lock?.close() lock?.close()
val lockType = lockType val lockType = mode.lockType
lock = if (lockType == null) null else WifiDoubleLock(lockType) lock = if (lockType == null) null else WifiDoubleLock(lockType)
} }
} }
} }
enum class Mode(val lockType: Int? = null) { enum class Mode(val lockType: Int? = null, val keepScreenOn: Boolean = false) {
None, Full(WifiManager.WIFI_MODE_FULL), HighPerf(WifiManager.WIFI_MODE_FULL_HIGH_PERF) None,
@Suppress("DEPRECATION")
@Deprecated("This constant was deprecated in API level Q.\n" +
"This API is non-functional and will have no impact.")
Full(WifiManager.WIFI_MODE_FULL),
HighPerf(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
@RequiresApi(29)
LowLatency(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, true),
}
class ActivityListener(val activity: ComponentActivity) :
LifecycleObserver, SharedPreferences.OnSharedPreferenceChangeListener {
private var keepScreenOn: Boolean = false
set(value) {
if (field == value) return
field = value
if (value) activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
init {
activity.lifecycle.addObserver(this)
app.pref.registerOnSharedPreferenceChangeListener(this)
keepScreenOn = mode.keepScreenOn
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == KEY) keepScreenOn = mode.keepScreenOn
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun shutdown() = app.pref.unregisterOnSharedPreferenceChangeListener(this)
} }
private val wifi = app.wifi.createWifiLock(lockType, "vpnhotspot:wifi").apply { acquire() } private val wifi = app.wifi.createWifiLock(lockType, "vpnhotspot:wifi").apply { acquire() }

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="settings_service_wifi_lock">
<item>@string/settings_service_wifi_lock_none</item>
<item>@string/settings_service_wifi_lock_high_perf_v29</item>
<item>@string/settings_service_wifi_lock_low_latency</item>
</string-array>
<string-array name="settings_service_wifi_lock_values">
<item>None</item>
<item>HighPerf</item>
<item>LowLatency</item>
</string-array>
</resources>

View File

@@ -101,6 +101,8 @@
<string name="settings_service_wifi_lock_none">System default</string> <string name="settings_service_wifi_lock_none">System default</string>
<string name="settings_service_wifi_lock_full">On</string> <string name="settings_service_wifi_lock_full">On</string>
<string name="settings_service_wifi_lock_high_perf">High Performance Mode</string> <string name="settings_service_wifi_lock_high_perf">High Performance Mode</string>
<string name="settings_service_wifi_lock_high_perf_v29">Disable power save</string>
<string name="settings_service_wifi_lock_low_latency">Low latency mode</string>
<string name="settings_service_ip_monitor">Network status monitor mode</string> <string name="settings_service_ip_monitor">Network status monitor mode</string>
<string name="settings_service_ip_monitor_monitor">Netlink monitor</string> <string name="settings_service_ip_monitor_monitor">Netlink monitor</string>
<string name="settings_service_ip_monitor_monitor_root">Netlink monitor with root</string> <string name="settings_service_ip_monitor_monitor_root">Netlink monitor with root</string>

View File

@@ -41,7 +41,6 @@
app:icon="@drawable/ic_device_wifi_lock" app:icon="@drawable/ic_device_wifi_lock"
app:entries="@array/settings_service_wifi_lock" app:entries="@array/settings_service_wifi_lock"
app:entryValues="@array/settings_service_wifi_lock_values" app:entryValues="@array/settings_service_wifi_lock_values"
app:defaultValue="Full"
app:title="@string/settings_service_wifi_lock" app:title="@string/settings_service_wifi_lock"
app:useSimpleSummaryProvider="true"/> app:useSimpleSummaryProvider="true"/>
<SwitchPreference <SwitchPreference