Support custom repeater SSID without root
This commit is contained in:
@@ -5,14 +5,12 @@ import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
import android.net.NetworkInfo
|
||||
import android.net.wifi.p2p.WifiP2pDevice
|
||||
import android.net.wifi.p2p.WifiP2pGroup
|
||||
import android.net.wifi.p2p.WifiP2pInfo
|
||||
import android.net.wifi.p2p.WifiP2pManager
|
||||
import android.net.wifi.p2p.*
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.os.BuildCompat
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
@@ -33,7 +31,10 @@ import java.lang.reflect.InvocationTargetException
|
||||
class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
companion object {
|
||||
private const val TAG = "RepeaterService"
|
||||
const val KEY_OPERATING_CHANNEL = "service.repeater.oc"
|
||||
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"
|
||||
private const val KEY_OPERATING_CHANNEL = "service.repeater.oc"
|
||||
|
||||
/**
|
||||
* This is only a "ServiceConnection" to system service and its impact on system is minimal.
|
||||
@@ -49,12 +50,23 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
val supported get() = p2pManager != null
|
||||
var persistentSupported = false
|
||||
|
||||
var networkName: String?
|
||||
get() = app.pref.getString(KEY_NETWORK_NAME, null)
|
||||
set(value) = app.pref.edit { putString(KEY_NETWORK_NAME, value) }
|
||||
var passphrase: String?
|
||||
get() = app.pref.getString(KEY_PASSPHRASE, null)
|
||||
set(value) = app.pref.edit { putString(KEY_PASSPHRASE, value) }
|
||||
var operatingBand: Int
|
||||
get() = app.pref.getInt(KEY_OPERATING_BAND, 0)
|
||||
set(value) = app.pref.edit { putInt(KEY_OPERATING_BAND, value) }
|
||||
var operatingChannel: Int
|
||||
get() {
|
||||
val result = app.pref.getString(KEY_OPERATING_CHANNEL, null)?.toIntOrNull() ?: 0
|
||||
return if (result in 1..165) result else 0
|
||||
}
|
||||
set(value) = app.pref.edit().putString(RepeaterService.KEY_OPERATING_CHANNEL, value.toString()).apply()
|
||||
set(value) = app.pref.edit { putString(RepeaterService.KEY_OPERATING_CHANNEL, value.toString()) }
|
||||
|
||||
private val networkNameField by lazy { WifiP2pConfig::class.java.getDeclaredField("networkName") }
|
||||
}
|
||||
|
||||
enum class Status {
|
||||
@@ -120,6 +132,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
}
|
||||
}
|
||||
private var routingManager: RoutingManager? = null
|
||||
private var persistNextGroup = false
|
||||
|
||||
var status = Status.IDLE
|
||||
private set(value) {
|
||||
@@ -245,12 +258,25 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
* startService Step 2 (if a group isn't already available)
|
||||
*/
|
||||
private fun doStart() = try {
|
||||
p2pManager.createGroup(channel, object : WifiP2pManager.ActionListener {
|
||||
val listener = object : WifiP2pManager.ActionListener {
|
||||
override fun onFailure(reason: Int) {
|
||||
startFailure(formatReason(R.string.repeater_create_group_failure, reason))
|
||||
}
|
||||
override fun onSuccess() { } // wait for WIFI_P2P_CONNECTION_CHANGED_ACTION to fire to go to step 3
|
||||
})
|
||||
}
|
||||
val networkName = networkName
|
||||
val passphrase = passphrase
|
||||
if (!BuildCompat.isAtLeastQ() || networkName == null || passphrase == null) {
|
||||
persistNextGroup = true
|
||||
p2pManager.createGroup(channel, listener)
|
||||
} else p2pManager.createGroup(channel, WifiP2pConfig.Builder().apply {
|
||||
setNetworkName("DIRECT-00-VPNHotspot") // placeholder for bypassing networkName check
|
||||
setPassphrase(passphrase)
|
||||
val frequency = operatingChannel
|
||||
if (frequency == 0) setGroupOperatingBand(operatingBand) else setGroupOperatingFrequency(frequency)
|
||||
}.build().apply {
|
||||
networkNameField.set(this, networkName)
|
||||
}, listener)
|
||||
} catch (e: SecurityException) {
|
||||
Timber.w(e)
|
||||
startFailure(e.readableMessage)
|
||||
@@ -276,6 +302,11 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
*/
|
||||
private fun doStart(group: WifiP2pGroup) {
|
||||
binder.group = group
|
||||
if (persistNextGroup) {
|
||||
networkName = group.networkName
|
||||
passphrase = group.passphrase
|
||||
persistNextGroup = false
|
||||
}
|
||||
check(routingManager == null)
|
||||
routingManager = RoutingManager.LocalOnly(this, group.`interface`!!).apply { start() }
|
||||
status = Status.ACTIVE
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.wifi.WifiConfiguration
|
||||
import android.net.wifi.p2p.WifiP2pConfig
|
||||
import android.net.wifi.p2p.WifiP2pGroup
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -32,6 +33,7 @@ import be.mygod.vpnhotspot.util.formatAddresses
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import timber.log.Timber
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
|
||||
@@ -112,6 +114,8 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("No longer used since Android Q")
|
||||
@Suppress("DEPRECATION")
|
||||
class ConfigHolder : ViewModel() {
|
||||
var config: P2pSupplicantConfiguration? = null
|
||||
}
|
||||
@@ -124,6 +128,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
private val data = Data()
|
||||
internal var binder: RepeaterService.Binder? = null
|
||||
private var p2pInterface: String? = null
|
||||
@Suppress("DEPRECATION")
|
||||
private val holder = ViewModelProviders.of(parent).get<ConfigHolder>()
|
||||
|
||||
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
|
||||
@@ -153,25 +158,51 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
}
|
||||
|
||||
val configuration: WifiConfiguration? get() {
|
||||
val group = binder?.group
|
||||
if (group != null) try {
|
||||
val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress)
|
||||
holder.config = config
|
||||
return newWifiApConfiguration(group.networkName, config.psk).apply {
|
||||
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) // is not actually used
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
apBand = AP_BAND_ANY
|
||||
if (BuildCompat.isAtLeastQ()) {
|
||||
val networkName = RepeaterService.networkName
|
||||
val passphrase = RepeaterService.passphrase
|
||||
if (networkName != null && passphrase != null) {
|
||||
return newWifiApConfiguration(networkName, passphrase).apply {
|
||||
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) // is not actually used
|
||||
apBand = when (RepeaterService.operatingBand) {
|
||||
WifiP2pConfig.GROUP_OWNER_BAND_AUTO -> AP_BAND_ANY
|
||||
WifiP2pConfig.GROUP_OWNER_BAND_2GHZ -> AP_BAND_2GHZ
|
||||
WifiP2pConfig.GROUP_OWNER_BAND_5GHZ -> AP_BAND_5GHZ
|
||||
else -> throw IllegalArgumentException("Unknown operatingBand")
|
||||
}
|
||||
apChannel = RepeaterService.operatingChannel
|
||||
}
|
||||
}
|
||||
} catch (e: RuntimeException) {
|
||||
Timber.w(e)
|
||||
} else @Suppress("DEPRECATION") {
|
||||
val group = binder?.group
|
||||
if (group != null) try {
|
||||
val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress)
|
||||
holder.config = config
|
||||
return newWifiApConfiguration(group.networkName, config.psk).apply {
|
||||
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) // is not actually used
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
apBand = AP_BAND_ANY
|
||||
apChannel = RepeaterService.operatingChannel
|
||||
}
|
||||
}
|
||||
} catch (e: RuntimeException) {
|
||||
Timber.w(e)
|
||||
}
|
||||
}
|
||||
SmartSnackbar.make(R.string.repeater_configure_failure).show()
|
||||
return null
|
||||
}
|
||||
fun updateConfiguration(config: WifiConfiguration) {
|
||||
holder.config?.let { master ->
|
||||
if (BuildCompat.isAtLeastQ()) {
|
||||
RepeaterService.networkName = config.SSID
|
||||
RepeaterService.passphrase = config.preSharedKey
|
||||
RepeaterService.operatingBand = when (config.apBand) {
|
||||
AP_BAND_ANY -> WifiP2pConfig.GROUP_OWNER_BAND_AUTO
|
||||
AP_BAND_2GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_2GHZ
|
||||
AP_BAND_5GHZ -> WifiP2pConfig.GROUP_OWNER_BAND_5GHZ
|
||||
else -> throw IllegalArgumentException("Unknown apBand")
|
||||
}
|
||||
} else @Suppress("DEPRECATION") holder.config?.let { master ->
|
||||
if (binder?.group?.networkName != config.SSID || master.psk != config.preSharedKey) try {
|
||||
master.update(config.SSID, config.preSharedKey)
|
||||
binder!!.group = null
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.lang.IllegalStateException
|
||||
* 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
|
||||
*/
|
||||
@Deprecated("No longer used since Android Q")
|
||||
class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress: String?) {
|
||||
companion object {
|
||||
private const val TAG = "P2pSupplicantConfiguration"
|
||||
|
||||
Reference in New Issue
Block a user