Support custom repeater SSID without root

This commit is contained in:
Mygod
2019-04-04 21:10:38 +08:00
parent 6b951519fe
commit e91abe0738
3 changed files with 82 additions and 19 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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"