Allow user to turn off safe mode on Android 10 (Mar 2020 or newer)

Fixes #153.

Basically, this "forward"-ports a workaround for Android 9- thanks to Jimmy Chen.
As a consequence, #31 might reoccur if you turn off safe mode.
This commit is contained in:
Mygod
2020-04-22 12:21:14 +08:00
parent 0dbf4b3b64
commit 3ba9a322c2
12 changed files with 121 additions and 88 deletions

View File

@@ -1,6 +1,7 @@
package be.mygod.vpnhotspot
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Service
import android.content.Intent
import android.content.SharedPreferences
@@ -24,11 +25,13 @@ import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.requestPersistentGroupI
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.setWifiP2pChannels
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps
import be.mygod.vpnhotspot.net.wifi.configuration.channelToFrequency
import be.mygod.vpnhotspot.room.macToString
import be.mygod.vpnhotspot.util.*
import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.coroutines.*
import timber.log.Timber
import java.lang.reflect.InvocationTargetException
import java.net.NetworkInterface
/**
* Service for handling Wi-Fi P2P. `supported` must be checked before this service is started otherwise it would crash.
@@ -36,6 +39,9 @@ import java.lang.reflect.InvocationTargetException
class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListener,
SharedPreferences.OnSharedPreferenceChangeListener {
companion object {
const val KEY_SAFE_MODE = "service.repeater.safeMode"
private const val KEY_LAST_MAC = "service.repeater.lastMac"
private const val KEY_NETWORK_NAME = "service.repeater.networkName"
private const val KEY_PASSPHRASE = "service.repeater.passphrase"
private const val KEY_OPERATING_BAND = "service.repeater.band"
@@ -57,9 +63,19 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
}
}
val supported get() = p2pManager != null
@Deprecated("Not initialized and no use at all since API 29")
var persistentSupported = false
private val hasP2pValidateName by lazy {
val (y, m, _) = Build.VERSION.SECURITY_PATCH.split('-', limit = 3).map { it.toInt() }
y > 2020 || y == 2020 && m >= 3
}
val safeModeConfigurable get() = Build.VERSION.SDK_INT >= 29 && hasP2pValidateName
val safeMode get() = Build.VERSION.SDK_INT >= 29 &&
(!hasP2pValidateName || app.pref.getBoolean(KEY_SAFE_MODE, true))
var lastMac: String?
get() = app.pref.getString(KEY_LAST_MAC, null)
private set(value) = app.pref.edit { putString(KEY_LAST_MAC, value) }
var networkName: String?
get() = app.pref.getString(KEY_NETWORK_NAME, null)
set(value) = app.pref.edit { putString(KEY_NETWORK_NAME, value) }
@@ -94,7 +110,6 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
}
}
val groupChanged = StickyEvent1 { group }
@Deprecated("Not initialized and no use at all since API 29")
var thisDevice: WifiP2pDevice? = null
fun startWps(pin: String? = null) {
@@ -136,13 +151,11 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP))
}
}
@Deprecated("No longer used since API 29")
@Suppress("DEPRECATION")
private val deviceListener = broadcastReceiver { _, intent ->
when (intent.action) {
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> binder.thisDevice =
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)
WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION -> onPersistentGroupsChanged()
WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION -> if (!safeMode) onPersistentGroupsChanged()
}
}
/**
@@ -174,17 +187,13 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
override fun onCreate() {
super.onCreate()
onChannelDisconnected()
if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") {
registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION,
WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION))
app.pref.registerOnSharedPreferenceChangeListener(this)
}
registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION,
WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION))
app.pref.registerOnSharedPreferenceChangeListener(this)
}
override fun onBind(intent: Intent) = binder
@Deprecated("No longer used since API 29")
@Suppress("DEPRECATION")
private fun setOperatingChannel(oc: Int = operatingChannel) = try {
val channel = channel
if (channel == null) SmartSnackbar.make(R.string.repeater_failure_disconnected).show()
@@ -207,21 +216,17 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
channel = null
if (status != Status.DESTROYED) try {
channel = p2pManager.initialize(this, Looper.getMainLooper(), this)
if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") setOperatingChannel()
if (!safeMode) setOperatingChannel()
} catch (e: RuntimeException) {
Timber.w(e)
handler.postDelayed(this::onChannelDisconnected, 1000)
}
}
@Deprecated("No longer used since API 29")
@Suppress("DEPRECATION")
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == KEY_OPERATING_CHANNEL) setOperatingChannel()
if (!safeMode && key == KEY_OPERATING_CHANNEL) setOperatingChannel()
}
@Deprecated("No longer used since API 29")
@Suppress("DEPRECATION")
private fun onPersistentGroupsChanged() {
val channel = channel ?: return
val device = binder.thisDevice ?: return
@@ -300,43 +305,45 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
val networkName = networkName
val passphrase = passphrase
try {
if (Build.VERSION.SDK_INT < 29 || networkName == null || passphrase == null) {
if (!safeMode || networkName == null || passphrase == null) {
persistNextGroup = true
p2pManager.createGroup(channel, listener)
} else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply {
setNetworkName(PLACEHOLDER_NETWORK_NAME)
setPassphrase(passphrase)
operatingChannel.let { oc ->
if (oc == 0) setGroupOperatingBand(operatingBand)
else setGroupOperatingFrequency(channelToFrequency(oc))
}
}.build().run {
useParcel { p ->
p.writeParcelable(this, 0)
val end = p.dataPosition()
p.setDataPosition(0)
val creator = p.readString()
val deviceAddress = p.readString()
val wps = p.readParcelable<WpsInfo>(javaClass.classLoader)
val long = p.readLong()
check(p.readString() == PLACEHOLDER_NETWORK_NAME)
check(p.readString() == passphrase)
val extrasLength = end - p.dataPosition()
check(extrasLength and 3 == 0) // parcel should be padded
if (extrasLength != 4) Timber.w(Exception("Unexpected extrasLength $extrasLength"))
val extras = (0 until extrasLength / 4).map { p.readInt() }
p.setDataPosition(0)
p.writeString(creator)
p.writeString(deviceAddress)
p.writeParcelable(wps, 0)
p.writeLong(long)
p.writeString(networkName)
p.writeString(passphrase)
extras.forEach(p::writeInt)
p.setDataPosition(0)
p.readParcelable<WifiP2pConfig>(javaClass.classLoader)
}
}, listener)
} else @TargetApi(29) {
p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply {
setNetworkName(PLACEHOLDER_NETWORK_NAME)
setPassphrase(passphrase)
operatingChannel.let { oc ->
if (oc == 0) setGroupOperatingBand(operatingBand)
else setGroupOperatingFrequency(channelToFrequency(oc))
}
}.build().run {
useParcel { p ->
p.writeParcelable(this, 0)
val end = p.dataPosition()
p.setDataPosition(0)
val creator = p.readString()
val deviceAddress = p.readString()
val wps = p.readParcelable<WpsInfo>(javaClass.classLoader)
val long = p.readLong()
check(p.readString() == PLACEHOLDER_NETWORK_NAME)
check(p.readString() == passphrase)
val extrasLength = end - p.dataPosition()
check(extrasLength and 3 == 0) // parcel should be padded
if (extrasLength != 4) Timber.w(Exception("Unexpected extrasLength $extrasLength"))
val extras = (0 until extrasLength / 4).map { p.readInt() }
p.setDataPosition(0)
p.writeString(creator)
p.writeString(deviceAddress)
p.writeParcelable(wps, 0)
p.writeLong(long)
p.writeString(networkName)
p.writeString(passphrase)
extras.forEach(p::writeInt)
p.setDataPosition(0)
p.readParcelable<WifiP2pConfig>(javaClass.classLoader)
}
}, listener)
}
} catch (e: SecurityException) {
Timber.w(e)
startFailure(e.readableMessage)
@@ -371,7 +378,11 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
persistNextGroup = false
}
check(routingManager == null)
routingManager = RoutingManager.LocalOnly(this, group.`interface`!!).apply { start() }
routingManager = object : RoutingManager.LocalOnly(this, group.`interface`!!) {
override fun ifaceHandler(iface: NetworkInterface) {
iface.hardwareAddress?.asIterable()?.macToString()?.let { lastMac = it }
}
}.apply { start() }
status = Status.ACTIVE
showNotification(group)
}
@@ -426,10 +437,8 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
cancel()
dispatcher.close()
}
if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") {
app.pref.unregisterOnSharedPreferenceChangeListener(this)
unregisterReceiver(deviceListener)
}
app.pref.unregisterOnSharedPreferenceChangeListener(this)
unregisterReceiver(deviceListener)
status = Status.DESTROYED
if (Build.VERSION.SDK_INT >= 27) channel?.close()
super.onDestroy()