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

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