Support Wi-Fi keep alives
This commit is contained in:
@@ -74,6 +74,7 @@ dependencies {
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.linkedin.dexmaker:dexmaker-mockito:2.19.1'
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
|
||||
implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.0.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
baseImplementation 'com.android.billingclient:billing:1.2'
|
||||
baseImplementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
tools:ignore="ProtectedPermissions"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.TETHER_PRIVILEGE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions"/>
|
||||
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
@@ -8,6 +8,7 @@ import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.manage.LocalOnlyHotspotManager
|
||||
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import timber.log.Timber
|
||||
@@ -30,6 +31,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() {
|
||||
private val binder = Binder()
|
||||
private var reservation: WifiManager.LocalOnlyHotspotReservation? = null
|
||||
private var routingManager: LocalOnlyInterfaceManager? = null
|
||||
private var locked = false
|
||||
private var receiverRegistered = false
|
||||
private val receiver = broadcastReceiver { _, intent ->
|
||||
val ifaces = TetheringManager.getLocalOnlyTetheredIfaces(intent.extras ?: return@broadcastReceiver)
|
||||
@@ -63,6 +65,9 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() {
|
||||
override fun onStarted(reservation: WifiManager.LocalOnlyHotspotReservation?) {
|
||||
if (reservation == null) onFailed(-2) else {
|
||||
this@LocalOnlyHotspotService.reservation = reservation
|
||||
check(!locked)
|
||||
WifiDoubleLock.acquire()
|
||||
locked = true
|
||||
if (!receiverRegistered) {
|
||||
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
|
||||
receiverRegistered = true
|
||||
@@ -119,6 +124,10 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService() {
|
||||
private fun unregisterReceiver() {
|
||||
routingManager?.stop()
|
||||
routingManager = null
|
||||
if (locked) {
|
||||
WifiDoubleLock.release()
|
||||
locked = false
|
||||
}
|
||||
if (receiverRegistered) {
|
||||
unregisterReceiver(receiver)
|
||||
IpNeighbourMonitor.unregisterCallback(this)
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.annotation.StringRes
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.os.postDelayed
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.deletePersistentGroup
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.netId
|
||||
@@ -123,6 +124,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
}
|
||||
}
|
||||
private var routingManager: LocalOnlyInterfaceManager? = null
|
||||
private var locked = false
|
||||
|
||||
var status = Status.IDLE
|
||||
private set(value) {
|
||||
@@ -261,6 +263,9 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
* startService Step 3
|
||||
*/
|
||||
private fun doStart(group: WifiP2pGroup) {
|
||||
check(!locked)
|
||||
WifiDoubleLock.acquire()
|
||||
locked = true
|
||||
binder.group = group
|
||||
check(routingManager == null)
|
||||
routingManager = LocalOnlyInterfaceManager(group.`interface`!!)
|
||||
@@ -297,6 +302,10 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
unregisterReceiver()
|
||||
routingManager?.stop()
|
||||
routingManager = null
|
||||
if (locked) {
|
||||
WifiDoubleLock.release()
|
||||
locked = false
|
||||
}
|
||||
status = Status.IDLE
|
||||
ServiceNotification.stopForeground(this)
|
||||
stopSelf()
|
||||
|
||||
@@ -5,8 +5,10 @@ import android.content.IntentFilter
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.manage.TetheringFragment
|
||||
import be.mygod.vpnhotspot.net.Routing
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import timber.log.Timber
|
||||
@@ -25,6 +27,7 @@ class TetheringService : IpNeighbourMonitoringService() {
|
||||
|
||||
private val binder = Binder()
|
||||
private val routings = HashMap<String, Routing?>()
|
||||
private var locked = false
|
||||
private var receiverRegistered = false
|
||||
private val receiver = broadcastReceiver { _, intent ->
|
||||
val extras = intent.extras ?: return@broadcastReceiver
|
||||
@@ -37,6 +40,10 @@ class TetheringService : IpNeighbourMonitoringService() {
|
||||
override val activeIfaces get() = synchronized(routings) { routings.keys.toList() }
|
||||
|
||||
private fun updateRoutingsLocked() {
|
||||
if (locked && routings.keys.all { !TetherType.ofInterface(it).isWifi }) {
|
||||
WifiDoubleLock.release()
|
||||
locked = false
|
||||
}
|
||||
if (routings.isEmpty()) {
|
||||
unregisterReceiver()
|
||||
ServiceNotification.stopForeground(this)
|
||||
@@ -92,7 +99,13 @@ class TetheringService : IpNeighbourMonitoringService() {
|
||||
if (intent != null) {
|
||||
val iface = intent.getStringExtra(EXTRA_ADD_INTERFACE)
|
||||
synchronized(routings) {
|
||||
if (iface != null) routings[iface] = null
|
||||
if (iface != null) {
|
||||
routings[iface] = null
|
||||
if (TetherType.ofInterface(iface).isWifi && !locked) {
|
||||
WifiDoubleLock.acquire()
|
||||
locked = true
|
||||
}
|
||||
}
|
||||
routings.remove(intent.getStringExtra(EXTRA_REMOVE_INTERFACE))?.revert()
|
||||
updateRoutingsLocked()
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ enum class TetherType {
|
||||
BLUETOOTH -> R.drawable.ic_device_bluetooth
|
||||
else -> R.drawable.ic_device_wifi_tethering
|
||||
}
|
||||
val isWifi get() = when (this) {
|
||||
WIFI_P2P, WIFI, WIMAX -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val usbRegexes: List<Pattern>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package be.mygod.vpnhotspot.net.wifi
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.SharedPreferences
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.PowerManager
|
||||
import androidx.core.content.getSystemService
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
|
||||
/**
|
||||
* This mechanism is used to maximize profit. Source: https://stackoverflow.com/a/29657230/2245107
|
||||
*/
|
||||
class WifiDoubleLock(lockType: Int) : AutoCloseable {
|
||||
companion object : SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private const val KEY = "service.wifiLock"
|
||||
private val lockType get() =
|
||||
WifiDoubleLock.Mode.valueOf(app.pref.getString(KEY, WifiDoubleLock.Mode.Full.toString()) ?: "").lockType
|
||||
private val service by lazy { app.getSystemService<PowerManager>()!! }
|
||||
|
||||
private var referenceCount = 0
|
||||
private var lock: WifiDoubleLock? = null
|
||||
|
||||
fun acquire() = synchronized(this) {
|
||||
if (referenceCount == 0) {
|
||||
app.pref.registerOnSharedPreferenceChangeListener(this)
|
||||
val lockType = lockType
|
||||
if (lockType != null) lock = WifiDoubleLock(lockType)
|
||||
}
|
||||
referenceCount += 1
|
||||
}
|
||||
fun release() = synchronized(this) {
|
||||
referenceCount -= 1
|
||||
if (referenceCount == 0) {
|
||||
lock?.close()
|
||||
lock = null
|
||||
app.pref.unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == KEY) synchronized(this) {
|
||||
lock?.close()
|
||||
val lockType = lockType
|
||||
lock = if (lockType == null) null else WifiDoubleLock(lockType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Mode(val lockType: Int? = null) {
|
||||
None, Full(WifiManager.WIFI_MODE_FULL), HighPerf(WifiManager.WIFI_MODE_FULL_HIGH_PERF)
|
||||
}
|
||||
|
||||
private val wifi = app.wifi.createWifiLock(lockType, "vpnhotspot:wifi").apply { acquire() }
|
||||
@SuppressLint("WakelockTimeout")
|
||||
private val power = service.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "vpnhotspot:power").apply { acquire() }
|
||||
|
||||
override fun close() {
|
||||
wifi.release()
|
||||
power.release()
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
android:viewportHeight="24.0"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20.5,9.5c0.28,0 0.55,0.04 0.81,0.08L24,6c-3.34,-2.51 -7.5,-4 -12,-4S3.34,3.49 0,6l12,16 3.5,-4.67L15.5,14.5c0,-2.76 2.24,-5 5,-5zM23,16v-1.5c0,-1.38 -1.12,-2.5 -2.5,-2.5S18,13.12 18,14.5L18,16c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zM22,16h-3v-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5L22,16z"/>
|
||||
|
||||
@@ -84,6 +84,10 @@
|
||||
<string name="settings_service_disable_ipv6">禁用 IPv6 共享</string>
|
||||
<string name="settings_service_disable_ipv6_summary">防止 IPv6 VPN 泄漏。</string>
|
||||
<string name="settings_service_repeater_start_on_boot">开机自启动中继</string>
|
||||
<string name="settings_service_wifi_lock">保持 Wi\u2011Fi 开启</string>
|
||||
<string name="settings_service_wifi_lock_none">系统默认</string>
|
||||
<string name="settings_service_wifi_lock_full">开</string>
|
||||
<string name="settings_service_wifi_lock_high_perf">高性能模式</string>
|
||||
<string name="settings_service_dns">备用 DNS 服务器[:端口]</string>
|
||||
<string name="settings_service_upstream">上游网络接口</string>
|
||||
<string name="settings_service_upstream_auto">自动检测系统 VPN</string>
|
||||
|
||||
13
mobile/src/main/res/values/arrays.xml
Normal file
13
mobile/src/main/res/values/arrays.xml
Normal 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_full</item>
|
||||
<item>@string/settings_service_wifi_lock_high_perf</item>
|
||||
</string-array>
|
||||
<string-array name="settings_service_wifi_lock_values">
|
||||
<item>None</item>
|
||||
<item>Full</item>
|
||||
<item>HighPerf</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -92,6 +92,10 @@
|
||||
<string name="settings_service_disable_ipv6">Disable IPv6 tethering</string>
|
||||
<string name="settings_service_disable_ipv6_summary">Enabling this option will prevent VPN leaks via IPv6.</string>
|
||||
<string name="settings_service_repeater_start_on_boot">Start repeater on boot</string>
|
||||
<string name="settings_service_wifi_lock">Keep Wi\u2011Fi alive</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_high_perf">High Performance Mode</string>
|
||||
<string name="settings_service_dns">Fallback DNS server[:port]</string>
|
||||
<string name="settings_service_upstream">Upstream network interface</string>
|
||||
<string name="settings_service_upstream_auto">Auto detect system VPN</string>
|
||||
|
||||
@@ -35,6 +35,14 @@
|
||||
android:icon="@drawable/ic_image_looks_6"
|
||||
android:title="@string/settings_service_disable_ipv6"
|
||||
android:summary="@string/settings_service_disable_ipv6_summary"/>
|
||||
<SimpleMenuPreference
|
||||
android:key="service.wifiLock"
|
||||
android:icon="@drawable/ic_device_wifi_lock"
|
||||
android:entries="@array/settings_service_wifi_lock"
|
||||
android:entryValues="@array/settings_service_wifi_lock_values"
|
||||
android:defaultValue="Full"
|
||||
android:summary="%s"
|
||||
android:title="@string/settings_service_wifi_lock"/>
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="service.dns"
|
||||
android:icon="@drawable/ic_action_dns"
|
||||
|
||||
Reference in New Issue
Block a user