Fix repeater stuck when Location is off on Android 11+
This commit is contained in:
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.ClipboardManager
|
||||
import android.content.res.Configuration
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.Size
|
||||
@@ -123,6 +124,7 @@ class App : Application() {
|
||||
}
|
||||
val pref by lazy { PreferenceManager.getDefaultSharedPreferences(deviceStorage) }
|
||||
val clipboard by lazy { getSystemService<ClipboardManager>()!! }
|
||||
val location by lazy { getSystemService<LocationManager>() }
|
||||
|
||||
val hasTouch by lazy { packageManager.hasSystemFeature("android.hardware.faketouch") }
|
||||
val customTabsIntent by lazy {
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.LocationManager
|
||||
import android.net.wifi.WpsInfo
|
||||
import android.net.wifi.p2p.*
|
||||
import android.os.Build
|
||||
@@ -20,7 +21,10 @@ import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor
|
||||
import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.deletePersistentGroup
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestConnectionInfo
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestDeviceAddress
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestGroupInfo
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestP2pState
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestPersistentGroupInfo
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.setWifiP2pChannels
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps
|
||||
@@ -215,6 +219,9 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> onP2pConnectionChanged(
|
||||
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO),
|
||||
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP))
|
||||
LocationManager.MODE_CHANGED_ACTION -> @TargetApi(30) {
|
||||
onLocationModeChanged(intent.getBooleanExtra(LocationManager.EXTRA_LOCATION_ENABLED, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
private val deviceListener = broadcastReceiver { _, intent ->
|
||||
@@ -311,6 +318,30 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
||||
}
|
||||
}
|
||||
|
||||
private var p2pPoller: Job? = null
|
||||
@RequiresApi(30)
|
||||
private fun onLocationModeChanged(enabled: Boolean) = if (enabled) p2pPoller?.cancel() else {
|
||||
SmartSnackbar.make(R.string.repeater_location_off).apply {
|
||||
action(R.string.repeater_location_off_configure) {
|
||||
it.context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
|
||||
}
|
||||
}.show()
|
||||
p2pPoller = launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
while (true) {
|
||||
delay(1000)
|
||||
val channel = channel ?: return@launch
|
||||
coroutineScope {
|
||||
launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
if (p2pManager.requestP2pState(channel) == WifiP2pManager.WIFI_P2P_STATE_DISABLED) cleanLocked()
|
||||
}
|
||||
val info = async(start = CoroutineStart.UNDISPATCHED) { p2pManager.requestConnectionInfo(channel) }
|
||||
val group = p2pManager.requestGroupInfo(channel)
|
||||
onP2pConnectionChanged(info.await(), group)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* startService Step 1
|
||||
*/
|
||||
@@ -321,29 +352,25 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
||||
// bump self to foreground location service (API 29+) to use location later, also to avoid getting killed
|
||||
if (Build.VERSION.SDK_INT >= 26) showNotification()
|
||||
launch {
|
||||
registerReceiver(receiver, intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
|
||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION))
|
||||
val filter = intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
|
||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
|
||||
if (Build.VERSION.SDK_INT >= 30) filter.addAction(LocationManager.MODE_CHANGED_ACTION)
|
||||
registerReceiver(receiver, filter)
|
||||
receiverRegistered = true
|
||||
try {
|
||||
p2pManager.requestGroupInfo(channel) {
|
||||
when {
|
||||
it == null -> doStart()
|
||||
it.isGroupOwner -> launch { if (routingManager == null) doStartLocked(it) }
|
||||
else -> {
|
||||
Timber.i("Removing old group ($it)")
|
||||
p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() {
|
||||
doStart()
|
||||
}
|
||||
override fun onFailure(reason: Int) =
|
||||
startFailure(formatReason(R.string.repeater_remove_old_group_failure, reason))
|
||||
})
|
||||
val group = p2pManager.requestGroupInfo(channel)
|
||||
when {
|
||||
group == null -> doStart()
|
||||
group.isGroupOwner -> if (routingManager == null) doStartLocked(group)
|
||||
else -> {
|
||||
Timber.i("Removing old group ($group)")
|
||||
p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() {
|
||||
launch { doStart() }
|
||||
}
|
||||
}
|
||||
override fun onFailure(reason: Int) =
|
||||
startFailure(formatReason(R.string.repeater_remove_old_group_failure, reason))
|
||||
})
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Timber.w(e)
|
||||
startFailure(e.readableMessage)
|
||||
}
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
@@ -351,15 +378,19 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
||||
/**
|
||||
* startService Step 2 (if a group isn't already available)
|
||||
*/
|
||||
private fun doStart() = launch {
|
||||
private suspend fun doStart() {
|
||||
val listener = object : WifiP2pManager.ActionListener {
|
||||
override fun onFailure(reason: Int) {
|
||||
startFailure(formatReason(R.string.repeater_create_group_failure, reason),
|
||||
showWifiEnable = reason == WifiP2pManager.BUSY)
|
||||
}
|
||||
override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3
|
||||
override fun onSuccess() {
|
||||
// wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3
|
||||
// in order for this to happen, we need to make sure that the callbacks are firing
|
||||
if (Build.VERSION.SDK_INT >= 30) onLocationModeChanged(app.location?.isLocationEnabled == true)
|
||||
}
|
||||
}
|
||||
val channel = channel ?: return@launch listener.onFailure(WifiP2pManager.BUSY)
|
||||
val channel = channel ?: return listener.onFailure(WifiP2pManager.BUSY)
|
||||
if (!safeMode) {
|
||||
binder.fetchPersistentGroup()
|
||||
setOperatingChannel()
|
||||
@@ -487,6 +518,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
||||
private fun cleanLocked() {
|
||||
if (receiverRegistered) {
|
||||
ensureReceiverUnregistered(receiver)
|
||||
p2pPoller?.cancel()
|
||||
receiverRegistered = false
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
|
||||
@@ -66,7 +66,7 @@ class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager()
|
||||
if (if (Build.VERSION.SDK_INT < 28) @Suppress("DEPRECATION") {
|
||||
Settings.Secure.getInt(context.contentResolver, Settings.Secure.LOCATION_MODE,
|
||||
Settings.Secure.LOCATION_MODE_OFF) == Settings.Secure.LOCATION_MODE_OFF
|
||||
} else context.getSystemService<LocationManager>()?.isLocationEnabled != true) try {
|
||||
} else app.location?.isLocationEnabled != true) try {
|
||||
context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
|
||||
Toast.makeText(context, R.string.tethering_temp_hotspot_location, Toast.LENGTH_LONG).show()
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.net.wifi
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.wifi.WpsInfo
|
||||
import android.net.wifi.p2p.WifiP2pGroup
|
||||
import android.net.wifi.p2p.WifiP2pInfo
|
||||
import android.net.wifi.p2p.WifiP2pManager
|
||||
import androidx.annotation.RequiresApi
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
@@ -129,7 +130,9 @@ object WifiP2pManagerHelper {
|
||||
return result.await()
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
suspend fun WifiP2pManager.requestConnectionInfo(c: WifiP2pManager.Channel) =
|
||||
CompletableDeferred<WifiP2pInfo?>().apply { requestConnectionInfo(c) { complete(it) } }.await()
|
||||
@SuppressLint("MissingPermission") // missing permission simply leads to null result
|
||||
@RequiresApi(29)
|
||||
suspend fun WifiP2pManager.requestDeviceAddress(c: WifiP2pManager.Channel): MacAddressCompat? {
|
||||
val future = CompletableDeferred<String?>()
|
||||
@@ -139,4 +142,10 @@ object WifiP2pManagerHelper {
|
||||
if (address == MacAddressCompat.ANY_ADDRESS) null else address
|
||||
}
|
||||
}
|
||||
@SuppressLint("MissingPermission") // missing permission simply leads to null result
|
||||
suspend fun WifiP2pManager.requestGroupInfo(c: WifiP2pManager.Channel) =
|
||||
CompletableDeferred<WifiP2pGroup?>().apply { requestGroupInfo(c) { complete(it) } }.await()
|
||||
@RequiresApi(29)
|
||||
suspend fun WifiP2pManager.requestP2pState(c: WifiP2pManager.Channel) =
|
||||
CompletableDeferred<Int>().apply { requestP2pState(c) { complete(it) } }.await()
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
<string name="repeater_failure_reason_unsupported_operation">不支持此操作</string>
|
||||
<string name="repeater_failure_disconnected">服务不可用,请稍后重试</string>
|
||||
<string name="repeater_missing_location_permissions">无线中继需要精确位置权限</string>
|
||||
<string name="repeater_location_off">由于系统限制,关闭位置信息服务可能产生问题并导致续航缩短</string>
|
||||
<string name="repeater_location_off_configure">进入设置</string>
|
||||
|
||||
<string name="tethering_temp_hotspot">临时 WLAN 热点</string>
|
||||
<string name="tethering_temp_hotspot_location">使用临时热点需要打开位置服务。</string>
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
<string name="repeater_failure_disconnected">Service unavailable. Try again later</string>
|
||||
<string name="repeater_missing_location_permissions">Repeater requires permissions for accessing fine
|
||||
location</string>
|
||||
<string name="repeater_location_off">Due to system restrictions, turning Location off may lead to things not working
|
||||
properly and increased battery usage</string>
|
||||
<string name="repeater_location_off_configure">Configure</string>
|
||||
|
||||
<string name="tethering_temp_hotspot">Temporary Wi\u2011Fi hotspot</string>
|
||||
<string name="tethering_temp_hotspot_location">Temporary hotspot requires location to be turned on.</string>
|
||||
|
||||
Reference in New Issue
Block a user