Support Wi-Fi keep alives

This commit is contained in:
Mygod
2018-12-23 23:35:55 +08:00
parent c4260638f2
commit 55443902be
12 changed files with 131 additions and 2 deletions

View File

@@ -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'

View File

@@ -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"/>

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()
}

View File

@@ -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>

View File

@@ -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()
}
}

View File

@@ -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"/>

View File

@@ -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>

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_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>

View File

@@ -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>

View File

@@ -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"