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:
@@ -1,6 +1,7 @@
|
|||||||
package be.mygod.vpnhotspot
|
package be.mygod.vpnhotspot
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.annotation.TargetApi
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
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.setWifiP2pChannels
|
||||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps
|
import be.mygod.vpnhotspot.net.wifi.WifiP2pManagerHelper.startWps
|
||||||
import be.mygod.vpnhotspot.net.wifi.configuration.channelToFrequency
|
import be.mygod.vpnhotspot.net.wifi.configuration.channelToFrequency
|
||||||
|
import be.mygod.vpnhotspot.room.macToString
|
||||||
import be.mygod.vpnhotspot.util.*
|
import be.mygod.vpnhotspot.util.*
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.reflect.InvocationTargetException
|
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.
|
* 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,
|
class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListener,
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
companion object {
|
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_NETWORK_NAME = "service.repeater.networkName"
|
||||||
private const val KEY_PASSPHRASE = "service.repeater.passphrase"
|
private const val KEY_PASSPHRASE = "service.repeater.passphrase"
|
||||||
private const val KEY_OPERATING_BAND = "service.repeater.band"
|
private const val KEY_OPERATING_BAND = "service.repeater.band"
|
||||||
@@ -57,9 +63,19 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val supported get() = p2pManager != null
|
val supported get() = p2pManager != null
|
||||||
@Deprecated("Not initialized and no use at all since API 29")
|
|
||||||
var persistentSupported = false
|
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?
|
var networkName: String?
|
||||||
get() = app.pref.getString(KEY_NETWORK_NAME, null)
|
get() = app.pref.getString(KEY_NETWORK_NAME, null)
|
||||||
set(value) = app.pref.edit { putString(KEY_NETWORK_NAME, value) }
|
set(value) = app.pref.edit { putString(KEY_NETWORK_NAME, value) }
|
||||||
@@ -94,7 +110,6 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val groupChanged = StickyEvent1 { group }
|
val groupChanged = StickyEvent1 { group }
|
||||||
@Deprecated("Not initialized and no use at all since API 29")
|
|
||||||
var thisDevice: WifiP2pDevice? = null
|
var thisDevice: WifiP2pDevice? = null
|
||||||
|
|
||||||
fun startWps(pin: String? = null) {
|
fun startWps(pin: String? = null) {
|
||||||
@@ -136,13 +151,11 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP))
|
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
private val deviceListener = broadcastReceiver { _, intent ->
|
private val deviceListener = broadcastReceiver { _, intent ->
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> binder.thisDevice =
|
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> binder.thisDevice =
|
||||||
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)
|
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() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
onChannelDisconnected()
|
onChannelDisconnected()
|
||||||
if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") {
|
|
||||||
registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION,
|
registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION,
|
||||||
WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION))
|
WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION))
|
||||||
app.pref.registerOnSharedPreferenceChangeListener(this)
|
app.pref.registerOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent) = binder
|
override fun onBind(intent: Intent) = binder
|
||||||
|
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
private fun setOperatingChannel(oc: Int = operatingChannel) = try {
|
private fun setOperatingChannel(oc: Int = operatingChannel) = try {
|
||||||
val channel = channel
|
val channel = channel
|
||||||
if (channel == null) SmartSnackbar.make(R.string.repeater_failure_disconnected).show()
|
if (channel == null) SmartSnackbar.make(R.string.repeater_failure_disconnected).show()
|
||||||
@@ -207,21 +216,17 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
channel = null
|
channel = null
|
||||||
if (status != Status.DESTROYED) try {
|
if (status != Status.DESTROYED) try {
|
||||||
channel = p2pManager.initialize(this, Looper.getMainLooper(), this)
|
channel = p2pManager.initialize(this, Looper.getMainLooper(), this)
|
||||||
if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") setOperatingChannel()
|
if (!safeMode) setOperatingChannel()
|
||||||
} catch (e: RuntimeException) {
|
} catch (e: RuntimeException) {
|
||||||
Timber.w(e)
|
Timber.w(e)
|
||||||
handler.postDelayed(this::onChannelDisconnected, 1000)
|
handler.postDelayed(this::onChannelDisconnected, 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
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() {
|
private fun onPersistentGroupsChanged() {
|
||||||
val channel = channel ?: return
|
val channel = channel ?: return
|
||||||
val device = binder.thisDevice ?: return
|
val device = binder.thisDevice ?: return
|
||||||
@@ -300,10 +305,11 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
val networkName = networkName
|
val networkName = networkName
|
||||||
val passphrase = passphrase
|
val passphrase = passphrase
|
||||||
try {
|
try {
|
||||||
if (Build.VERSION.SDK_INT < 29 || networkName == null || passphrase == null) {
|
if (!safeMode || networkName == null || passphrase == null) {
|
||||||
persistNextGroup = true
|
persistNextGroup = true
|
||||||
p2pManager.createGroup(channel, listener)
|
p2pManager.createGroup(channel, listener)
|
||||||
} else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply {
|
} else @TargetApi(29) {
|
||||||
|
p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply {
|
||||||
setNetworkName(PLACEHOLDER_NETWORK_NAME)
|
setNetworkName(PLACEHOLDER_NETWORK_NAME)
|
||||||
setPassphrase(passphrase)
|
setPassphrase(passphrase)
|
||||||
operatingChannel.let { oc ->
|
operatingChannel.let { oc ->
|
||||||
@@ -337,6 +343,7 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
p.readParcelable<WifiP2pConfig>(javaClass.classLoader)
|
p.readParcelable<WifiP2pConfig>(javaClass.classLoader)
|
||||||
}
|
}
|
||||||
}, listener)
|
}, listener)
|
||||||
|
}
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
Timber.w(e)
|
Timber.w(e)
|
||||||
startFailure(e.readableMessage)
|
startFailure(e.readableMessage)
|
||||||
@@ -371,7 +378,11 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
persistNextGroup = false
|
persistNextGroup = false
|
||||||
}
|
}
|
||||||
check(routingManager == null)
|
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
|
status = Status.ACTIVE
|
||||||
showNotification(group)
|
showNotification(group)
|
||||||
}
|
}
|
||||||
@@ -426,10 +437,8 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene
|
|||||||
cancel()
|
cancel()
|
||||||
dispatcher.close()
|
dispatcher.close()
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT < 29) @Suppress("DEPRECATION") {
|
|
||||||
app.pref.unregisterOnSharedPreferenceChangeListener(this)
|
app.pref.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
unregisterReceiver(deviceListener)
|
unregisterReceiver(deviceListener)
|
||||||
}
|
|
||||||
status = Status.DESTROYED
|
status = Status.DESTROYED
|
||||||
if (Build.VERSION.SDK_INT >= 27) channel?.close()
|
if (Build.VERSION.SDK_INT >= 27) channel?.close()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import be.mygod.vpnhotspot.net.Routing
|
|||||||
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
|
||||||
abstract class RoutingManager(private val caller: Any, val downstream: String, private val isWifi: Boolean) {
|
abstract class RoutingManager(private val caller: Any, val downstream: String, private val isWifi: Boolean) {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -42,7 +43,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
|||||||
/**
|
/**
|
||||||
* Both repeater and local-only hotspot are Wi-Fi based.
|
* Both repeater and local-only hotspot are Wi-Fi based.
|
||||||
*/
|
*/
|
||||||
class LocalOnly(caller: Any, downstream: String) : RoutingManager(caller, downstream, true) {
|
open class LocalOnly(caller: Any, downstream: String) : RoutingManager(caller, downstream, true) {
|
||||||
override fun Routing.configure() {
|
override fun Routing.configure() {
|
||||||
ipForward() // local only interfaces need to enable ip_forward
|
ipForward() // local only interfaces need to enable ip_forward
|
||||||
forward()
|
forward()
|
||||||
@@ -63,8 +64,10 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
|||||||
else -> error("Double routing detected for $downstream from $caller != ${other.caller}")
|
else -> error("Double routing detected for $downstream from $caller != ${other.caller}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun ifaceHandler(iface: NetworkInterface) { }
|
||||||
|
|
||||||
private fun initRouting() = try {
|
private fun initRouting() = try {
|
||||||
routing = Routing(caller, downstream).apply {
|
routing = Routing(caller, downstream, this::ifaceHandler).apply {
|
||||||
try {
|
try {
|
||||||
configure()
|
configure()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
boot.isChecked = BootReceiver.enabled
|
boot.isChecked = BootReceiver.enabled
|
||||||
} else boot.parent!!.removePreference(boot)
|
} else boot.parent!!.removePreference(boot)
|
||||||
|
if (!RepeaterService.supported || !RepeaterService.safeModeConfigurable) {
|
||||||
|
val safeMode = findPreference<Preference>(RepeaterService.KEY_SAFE_MODE)!!
|
||||||
|
safeMode.parent!!.removePreference(safeMode)
|
||||||
|
}
|
||||||
findPreference<Preference>("service.clean")!!.setOnPreferenceClickListener {
|
findPreference<Preference>("service.clean")!!.setOnPreferenceClickListener {
|
||||||
GlobalScope.launch { RoutingManager.clean() }
|
GlobalScope.launch { RoutingManager.clean() }
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
|
|
||||||
val title: CharSequence @Bindable get() {
|
val title: CharSequence @Bindable get() {
|
||||||
if (Build.VERSION.SDK_INT >= 29) binder?.group?.frequency?.let {
|
if (Build.VERSION.SDK_INT >= 29) binder?.group?.frequency?.let {
|
||||||
return parent.getString(R.string.repeater_channel, it, frequencyToChannel(it))
|
if (it != 0) return parent.getString(R.string.repeater_channel, it, frequencyToChannel(it))
|
||||||
}
|
}
|
||||||
return parent.getString(R.string.title_repeater)
|
return parent.getString(R.string.title_repeater)
|
||||||
}
|
}
|
||||||
@@ -120,8 +120,6 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
class ConfigHolder : ViewModel() {
|
class ConfigHolder : ViewModel() {
|
||||||
var config: P2pSupplicantConfiguration? = null
|
var config: P2pSupplicantConfiguration? = null
|
||||||
}
|
}
|
||||||
@@ -134,7 +132,6 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
private val data = Data()
|
private val data = Data()
|
||||||
internal var binder: RepeaterService.Binder? = null
|
internal var binder: RepeaterService.Binder? = null
|
||||||
private var p2pInterface: String? = null
|
private var p2pInterface: String? = null
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
private val holder by parent.viewModels<ConfigHolder>()
|
private val holder by parent.viewModels<ConfigHolder>()
|
||||||
|
|
||||||
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
|
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
|
||||||
@@ -164,7 +161,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
}
|
}
|
||||||
|
|
||||||
val configuration: WifiConfiguration? get() {
|
val configuration: WifiConfiguration? get() {
|
||||||
if (Build.VERSION.SDK_INT >= 29) {
|
if (RepeaterService.safeMode) {
|
||||||
val networkName = RepeaterService.networkName
|
val networkName = RepeaterService.networkName
|
||||||
val passphrase = RepeaterService.passphrase
|
val passphrase = RepeaterService.passphrase
|
||||||
if (networkName != null && passphrase != null) {
|
if (networkName != null && passphrase != null) {
|
||||||
@@ -179,7 +176,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
apChannel = RepeaterService.operatingChannel
|
apChannel = RepeaterService.operatingChannel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else @Suppress("DEPRECATION") {
|
} else {
|
||||||
val group = binder?.group
|
val group = binder?.group
|
||||||
if (group != null) try {
|
if (group != null) try {
|
||||||
val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress)
|
val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress)
|
||||||
@@ -199,7 +196,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
suspend fun updateConfiguration(config: WifiConfiguration) {
|
suspend fun updateConfiguration(config: WifiConfiguration) {
|
||||||
if (Build.VERSION.SDK_INT >= 29) {
|
if (RepeaterService.safeMode) {
|
||||||
RepeaterService.networkName = config.SSID
|
RepeaterService.networkName = config.SSID
|
||||||
RepeaterService.passphrase = config.preSharedKey
|
RepeaterService.passphrase = config.preSharedKey
|
||||||
RepeaterService.operatingBand = when (config.apBand) {
|
RepeaterService.operatingBand = when (config.apBand) {
|
||||||
@@ -208,7 +205,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
AP_BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ
|
AP_BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ
|
||||||
else -> throw IllegalArgumentException("Unknown apBand")
|
else -> throw IllegalArgumentException("Unknown apBand")
|
||||||
}
|
}
|
||||||
} else @Suppress("DEPRECATION") holder.config?.let { master ->
|
} else holder.config?.let { master ->
|
||||||
if (binder?.group?.networkName != config.SSID || master.psk != config.preSharedKey) try {
|
if (binder?.group?.networkName != config.SSID || master.psk != config.preSharedKey) try {
|
||||||
withContext(Dispatchers.Default) { master.update(config.SSID, config.preSharedKey) }
|
withContext(Dispatchers.Default) { master.update(config.SSID, config.preSharedKey) }
|
||||||
binder!!.group = null
|
binder!!.group = null
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ import java.net.SocketException
|
|||||||
*
|
*
|
||||||
* Once revert is called, this object no longer serves any purpose.
|
* Once revert is called, this object no longer serves any purpose.
|
||||||
*/
|
*/
|
||||||
class Routing(private val caller: Any, private val downstream: String) : IpNeighbourMonitor.Callback {
|
class Routing(private val caller: Any, private val downstream: String,
|
||||||
|
ifaceHandler: (NetworkInterface) -> Unit) : IpNeighbourMonitor.Callback {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Since Android 5.0, RULE_PRIORITY_TETHERING = 18000.
|
* Since Android 5.0, RULE_PRIORITY_TETHERING = 18000.
|
||||||
@@ -117,8 +118,9 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val hostAddress = try {
|
private val hostAddress = try {
|
||||||
val addresses = NetworkInterface.getByName(downstream)!!.interfaceAddresses!!
|
val iface = NetworkInterface.getByName(downstream)!!
|
||||||
.filter { it.address is Inet4Address }
|
ifaceHandler(iface)
|
||||||
|
val addresses = iface.interfaceAddresses!!.filter { it.address is Inet4Address }
|
||||||
if (addresses.size > 1) error("More than one addresses was found: $addresses")
|
if (addresses.size > 1) error("More than one addresses was found: $addresses")
|
||||||
addresses.first()
|
addresses.first()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import java.lang.reflect.Proxy
|
|||||||
|
|
||||||
object WifiP2pManagerHelper {
|
object WifiP2pManagerHelper {
|
||||||
const val UNSUPPORTED = -2
|
const val UNSUPPORTED = -2
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
const val WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION = "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED"
|
const val WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION = "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,7 +23,6 @@ object WifiP2pManagerHelper {
|
|||||||
WifiP2pManager::class.java.getDeclaredMethod("setWifiP2pChannels", WifiP2pManager.Channel::class.java,
|
WifiP2pManager::class.java.getDeclaredMethod("setWifiP2pChannels", WifiP2pManager.Channel::class.java,
|
||||||
Int::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
|
Int::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
|
||||||
}
|
}
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
fun WifiP2pManager.setWifiP2pChannels(c: WifiP2pManager.Channel, lc: Int, oc: Int,
|
fun WifiP2pManager.setWifiP2pChannels(c: WifiP2pManager.Channel, lc: Int, oc: Int,
|
||||||
listener: WifiP2pManager.ActionListener) {
|
listener: WifiP2pManager.ActionListener) {
|
||||||
try {
|
try {
|
||||||
@@ -63,7 +61,6 @@ object WifiP2pManagerHelper {
|
|||||||
WifiP2pManager::class.java.getDeclaredMethod("deletePersistentGroup",
|
WifiP2pManager::class.java.getDeclaredMethod("deletePersistentGroup",
|
||||||
WifiP2pManager.Channel::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
|
WifiP2pManager.Channel::class.java, Int::class.java, WifiP2pManager.ActionListener::class.java)
|
||||||
}
|
}
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
fun WifiP2pManager.deletePersistentGroup(c: WifiP2pManager.Channel, netId: Int,
|
fun WifiP2pManager.deletePersistentGroup(c: WifiP2pManager.Channel, netId: Int,
|
||||||
listener: WifiP2pManager.ActionListener) {
|
listener: WifiP2pManager.ActionListener) {
|
||||||
try {
|
try {
|
||||||
@@ -90,7 +87,6 @@ object WifiP2pManagerHelper {
|
|||||||
* @param c is the channel created at {@link #initialize}
|
* @param c is the channel created at {@link #initialize}
|
||||||
* @param listener for callback when persistent group info list is available. Can be null.
|
* @param listener for callback when persistent group info list is available. Can be null.
|
||||||
*/
|
*/
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
fun WifiP2pManager.requestPersistentGroupInfo(c: WifiP2pManager.Channel,
|
fun WifiP2pManager.requestPersistentGroupInfo(c: WifiP2pManager.Channel,
|
||||||
listener: (Collection<WifiP2pGroup>) -> Unit) {
|
listener: (Collection<WifiP2pGroup>) -> Unit) {
|
||||||
val proxy = Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader,
|
val proxy = Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader,
|
||||||
@@ -115,6 +111,5 @@ object WifiP2pManagerHelper {
|
|||||||
private val getNetworkId by lazy @SuppressLint("DiscouragedPrivateApi") {
|
private val getNetworkId by lazy @SuppressLint("DiscouragedPrivateApi") {
|
||||||
WifiP2pGroup::class.java.getDeclaredMethod("getNetworkId")
|
WifiP2pGroup::class.java.getDeclaredMethod("getNetworkId")
|
||||||
}
|
}
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
val WifiP2pGroup.netId get() = getNetworkId.invoke(this) as Int
|
val WifiP2pGroup.netId get() = getNetworkId.invoke(this) as Int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import java.io.File
|
|||||||
* https://android.googlesource.com/platform/external/wpa_supplicant_8/+/d2986c2/wpa_supplicant/config.c#488
|
* https://android.googlesource.com/platform/external/wpa_supplicant_8/+/d2986c2/wpa_supplicant/config.c#488
|
||||||
* https://android.googlesource.com/platform/external/wpa_supplicant_8/+/6fa46df/wpa_supplicant/config_file.c#182
|
* https://android.googlesource.com/platform/external/wpa_supplicant_8/+/6fa46df/wpa_supplicant/config_file.c#182
|
||||||
*/
|
*/
|
||||||
@Deprecated("No longer used since API 29")
|
|
||||||
class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress: String?) {
|
class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress: String?) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "P2pSupplicantConfiguration"
|
private const val TAG = "P2pSupplicantConfiguration"
|
||||||
@@ -29,7 +28,7 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress:
|
|||||||
var pskLine: Int? = null
|
var pskLine: Int? = null
|
||||||
var psk: String? = null
|
var psk: String? = null
|
||||||
var groupOwner = false
|
var groupOwner = false
|
||||||
var bssidMatches = false
|
var bssid: String? = null
|
||||||
|
|
||||||
override fun toString() = joinToString("\n")
|
override fun toString() = joinToString("\n")
|
||||||
}
|
}
|
||||||
@@ -53,9 +52,9 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress:
|
|||||||
RootSession.checkOutput(command, shell, false, false)
|
RootSession.checkOutput(command, shell, false, false)
|
||||||
val parser = Parser(shell.out)
|
val parser = Parser(shell.out)
|
||||||
try {
|
try {
|
||||||
val bssids = listOfNotNull(group.owner.deviceAddress, ownerAddress)
|
val bssids = listOfNotNull(group.owner.deviceAddress, ownerAddress, RepeaterService.lastMac)
|
||||||
.distinct()
|
.distinct()
|
||||||
.filter { it != "00:00:00:00:00:00" }
|
.filter { it != "00:00:00:00:00:00" && it != "02:00:00:00:00:00" }
|
||||||
while (parser.next()) {
|
while (parser.next()) {
|
||||||
if (parser.trimmed.startsWith("network={")) {
|
if (parser.trimmed.startsWith("network={")) {
|
||||||
val block = NetworkBlock()
|
val block = NetworkBlock()
|
||||||
@@ -73,21 +72,20 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress:
|
|||||||
block.psk = match.groupValues[5].apply { check(length in 8..63) }
|
block.psk = match.groupValues[5].apply { check(length in 8..63) }
|
||||||
}
|
}
|
||||||
block.pskLine = block.size
|
block.pskLine = block.size
|
||||||
} else if (bssids.any { matchedBssid.equals(it, true) }) block.bssidMatches = true
|
} else if (bssids.any { matchedBssid.equals(it, true) }) block.bssid = matchedBssid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block.add(parser.line)
|
block.add(parser.line)
|
||||||
}
|
}
|
||||||
block.add(parser.line)
|
block.add(parser.line)
|
||||||
result.add(block)
|
result.add(block)
|
||||||
if (block.bssidMatches && block.groupOwner && target == null) { // keep first only
|
if (block.bssid != null && block.groupOwner && target == null) { // keep first only
|
||||||
check(block.ssidLine != null && block.pskLine != null)
|
check(block.ssidLine != null && block.pskLine != null)
|
||||||
target = block
|
target = block
|
||||||
}
|
}
|
||||||
} else result.add(parser.line)
|
} else result.add(parser.line)
|
||||||
}
|
}
|
||||||
if (target == null && !RepeaterService.persistentSupported) {
|
if (target == null && !RepeaterService.persistentSupported) {
|
||||||
val bssid = bssids.single()
|
|
||||||
result.add("")
|
result.add("")
|
||||||
result.add(NetworkBlock().apply {
|
result.add(NetworkBlock().apply {
|
||||||
// generate a basic network block, it is likely that vendor is going to add more stuff here
|
// generate a basic network block, it is likely that vendor is going to add more stuff here
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import be.mygod.vpnhotspot.util.toByteArray
|
|||||||
import be.mygod.vpnhotspot.util.toParcelable
|
import be.mygod.vpnhotspot.util.toParcelable
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import java.nio.charset.Charset
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Based on: https://android.googlesource.com/platform/packages/apps/Settings/+/39b4674/src/com/android/settings/wifi/WifiApDialog.java
|
* Based on: https://android.googlesource.com/platform/packages/apps/Settings/+/39b4674/src/com/android/settings/wifi/WifiApDialog.java
|
||||||
@@ -117,7 +116,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
bandOptions = mutableListOf<BandOption>().apply {
|
bandOptions = mutableListOf<BandOption>().apply {
|
||||||
if (arg.p2pMode) {
|
if (arg.p2pMode) {
|
||||||
add(BandOption.BandAny)
|
add(BandOption.BandAny)
|
||||||
if (Build.VERSION.SDK_INT >= 29) {
|
if (RepeaterService.safeMode) {
|
||||||
add(BandOption.Band2GHz)
|
add(BandOption.Band2GHz)
|
||||||
add(BandOption.Band5GHz)
|
add(BandOption.Band5GHz)
|
||||||
}
|
}
|
||||||
@@ -160,8 +159,10 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
*/
|
*/
|
||||||
private fun validate() {
|
private fun validate() {
|
||||||
if (!started) return
|
if (!started) return
|
||||||
val ssidValid = dialogView.ssid.length() != 0 &&
|
val ssidLength = dialogView.ssid.text.toString().toByteArray().size
|
||||||
Charset.forName("UTF-8").encode(dialogView.ssid.text.toString()).limit() <= 32
|
dialogView.ssidWrapper.error = if (RepeaterService.safeModeConfigurable && ssidLength < 9) {
|
||||||
|
requireContext().getString(R.string.settings_service_repeater_safe_mode_warning)
|
||||||
|
} else null
|
||||||
val passwordValid = when (selectedSecurity) {
|
val passwordValid = when (selectedSecurity) {
|
||||||
WifiConfiguration.KeyMgmt.WPA_PSK, WPA2_PSK -> dialogView.password.length() >= 8
|
WifiConfiguration.KeyMgmt.WPA_PSK, WPA2_PSK -> dialogView.password.length() >= 8
|
||||||
else -> true // do not try to validate
|
else -> true // do not try to validate
|
||||||
@@ -169,7 +170,8 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
dialogView.passwordWrapper.error = if (passwordValid) null else {
|
dialogView.passwordWrapper.error = if (passwordValid) null else {
|
||||||
requireContext().getString(R.string.credentials_password_too_short)
|
requireContext().getString(R.string.credentials_password_too_short)
|
||||||
}
|
}
|
||||||
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = ssidValid && passwordValid
|
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled =
|
||||||
|
ssidLength in 1..32 && passwordValid
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { }
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { }
|
||||||
|
|||||||
10
mobile/src/main/res/drawable/ic_alert_warning.xml
Normal file
10
mobile/src/main/res/drawable/ic_alert_warning.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
|
||||||
|
</vector>
|
||||||
@@ -87,6 +87,9 @@
|
|||||||
<string name="settings_service_disable_ipv6">禁用 IPv6 共享</string>
|
<string name="settings_service_disable_ipv6">禁用 IPv6 共享</string>
|
||||||
<string name="settings_service_disable_ipv6_summary">防止 VPN 通过 IPv6 泄漏。</string>
|
<string name="settings_service_disable_ipv6_summary">防止 VPN 通过 IPv6 泄漏。</string>
|
||||||
<string name="settings_service_repeater_start_on_boot">开机自启动中继</string>
|
<string name="settings_service_repeater_start_on_boot">开机自启动中继</string>
|
||||||
|
<string name="settings_service_repeater_safe_mode">中继安全模式</string>
|
||||||
|
<string name="settings_service_repeater_safe_mode_summary">不对系统配置进行修改,但是可能须要较长的网络名称。</string>
|
||||||
|
<string name="settings_service_repeater_safe_mode_warning">使用短名称可能需要关闭安全模式。</string>
|
||||||
<string name="settings_service_wifi_lock">保持 Wi\u2011Fi 开启</string>
|
<string name="settings_service_wifi_lock">保持 Wi\u2011Fi 开启</string>
|
||||||
<string name="settings_service_wifi_lock_none">系统默认</string>
|
<string name="settings_service_wifi_lock_none">系统默认</string>
|
||||||
<string name="settings_service_wifi_lock_full">开</string>
|
<string name="settings_service_wifi_lock_full">开</string>
|
||||||
|
|||||||
@@ -92,6 +92,10 @@
|
|||||||
<string name="settings_service_disable_ipv6">Disable IPv6 tethering</string>
|
<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_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_repeater_start_on_boot">Start repeater on boot</string>
|
||||||
|
<string name="settings_service_repeater_safe_mode">Repeater safe mode</string>
|
||||||
|
<string name="settings_service_repeater_safe_mode_summary">Makes no changes to your system configuration but might
|
||||||
|
not work with short SSIDs.</string>
|
||||||
|
<string name="settings_service_repeater_safe_mode_warning">Short SSIDs might require turning off safe mode.</string>
|
||||||
<string name="settings_service_wifi_lock">Keep Wi\u2011Fi alive</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_none">System default</string>
|
||||||
<string name="settings_service_wifi_lock_full">On</string>
|
<string name="settings_service_wifi_lock_full">On</string>
|
||||||
|
|||||||
@@ -62,6 +62,12 @@
|
|||||||
app:key="service.repeater.startOnBoot"
|
app:key="service.repeater.startOnBoot"
|
||||||
app:icon="@drawable/ic_action_autorenew"
|
app:icon="@drawable/ic_action_autorenew"
|
||||||
app:title="@string/settings_service_repeater_start_on_boot"/>
|
app:title="@string/settings_service_repeater_start_on_boot"/>
|
||||||
|
<SwitchPreference
|
||||||
|
app:key="service.repeater.safeMode"
|
||||||
|
app:icon="@drawable/ic_alert_warning"
|
||||||
|
app:title="@string/settings_service_repeater_safe_mode"
|
||||||
|
app:summary="@string/settings_service_repeater_safe_mode_summary"
|
||||||
|
app:defaultValue="true"/>
|
||||||
<com.takisoft.preferencex.SimpleMenuPreference
|
<com.takisoft.preferencex.SimpleMenuPreference
|
||||||
app:key="service.ipMonitor"
|
app:key="service.ipMonitor"
|
||||||
app:icon="@drawable/ic_hardware_device_hub"
|
app:icon="@drawable/ic_hardware_device_hub"
|
||||||
|
|||||||
Reference in New Issue
Block a user