librootkotlinx

Fixes #14, #27, #114, #117.
This commit is contained in:
Mygod
2020-06-21 05:33:39 +08:00
parent 7b1f610f9a
commit ad218d7ec6
51 changed files with 1781 additions and 574 deletions

View File

@@ -1,24 +1,20 @@
package be.mygod.vpnhotspot.net.wifi
import android.net.wifi.p2p.WifiP2pGroup
import android.os.Build
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.RepeaterService
import be.mygod.vpnhotspot.net.MacAddressCompat
import be.mygod.vpnhotspot.util.RootSession
import be.mygod.vpnhotspot.root.RepeaterCommands
import be.mygod.vpnhotspot.root.RootManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import java.io.File
/**
* This parser is based on:
* 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
*/
class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null, ownerAddress: String? = null) {
class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null) {
companion object {
private const val TAG = "P2pSupplicantConfiguration"
private const val CONF_PATH_TREBLE = "/data/vendor/wifi/wpa/p2p_supplicant.conf"
private const val CONF_PATH_LEGACY = "/data/misc/wifi/p2p_supplicant.conf"
private const val PERSISTENT_MAC = "p2p_device_persistent_mac_addr="
private val networkParser =
"^(bssid=(([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2})|psk=(ext:|\"(.*)\"|[0-9a-fA-F]{64}\$)?)".toRegex()
@@ -36,12 +32,11 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null, ownerA
override fun toString() = joinToString("\n")
}
private class Parser(val lines: List<String>) {
private val iterator = lines.iterator()
private class Parser(val lines: Iterator<String>) {
lateinit var line: String
lateinit var trimmed: String
fun next() = if (iterator.hasNext()) {
line = iterator.next().apply { trimmed = trimStart('\r', '\t', ' ') }
fun next() = if (lines.hasNext()) {
line = lines.next().apply { trimmed = trimStart('\r', '\t', ' ') }
true
} else false
}
@@ -49,14 +44,12 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null, ownerA
private data class Content(val lines: ArrayList<Any>, var target: NetworkBlock, var persistentMacLine: Int?,
var legacy: Boolean)
private val content = RootSession.use {
private lateinit var content: Content
suspend fun init(ownerAddress: String? = null) {
val result = ArrayList<Any>()
var target: NetworkBlock? = null
var persistentMacLine: Int? = null
val command = "cat $CONF_PATH_TREBLE || cat $CONF_PATH_LEGACY"
val shell = it.execQuiet(command)
RootSession.checkOutput(command, shell, false, false)
val parser = Parser(shell.out)
val (config, legacy) = RootManager.use { it.execute(RepeaterCommands.ReadP2pConfig()) }
try {
var bssids = listOfNotNull(group?.owner?.deviceAddress, ownerAddress)
.distinct()
@@ -68,6 +61,7 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null, ownerA
false
}
}
val parser = Parser(config.lineSequence().iterator())
while (parser.next()) {
if (parser.trimmed.startsWith("network={")) {
val block = NetworkBlock()
@@ -129,22 +123,22 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null, ownerA
if (target == null) target = this
})
}
Content(result, target!!.apply {
content = Content(result, target!!.apply {
RepeaterService.lastMac = bssid!!
}, persistentMacLine, shell.err.isNotEmpty())
} catch (e: RuntimeException) {
}, persistentMacLine, legacy)
} catch (e: Exception) {
FirebaseCrashlytics.getInstance().apply {
setCustomKey(TAG, parser.lines.joinToString("\n"))
setCustomKey(TAG, config)
setCustomKey("$TAG.ownerAddress", ownerAddress.toString())
setCustomKey("$TAG.p2pGroup", group.toString())
}
throw e
}
}
val psk = group?.passphrase ?: content.target.psk!!
val bssid = MacAddressCompat.fromString(content.target.bssid!!)
val psk by lazy { group?.passphrase ?: content.target.psk!! }
val bssid by lazy { MacAddressCompat.fromString(content.target.bssid!!) }
fun update(ssid: String, psk: String, bssid: MacAddressCompat?) {
suspend fun update(ssid: String, psk: String, bssid: MacAddressCompat?) {
val (lines, block, persistentMacLine, legacy) = content
block[block.ssidLine!!] = "\tssid=" + ssid.toByteArray()
.joinToString("") { (it.toInt() and 255).toString(16).padStart(2, '0') }
@@ -153,25 +147,6 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null, ownerA
persistentMacLine?.let { lines[it] = PERSISTENT_MAC + bssid }
block[block.bssidLine!!] = "\tbssid=$bssid"
}
val tempFile = File.createTempFile("vpnhotspot-", ".conf", app.deviceStorage.cacheDir)
try {
tempFile.printWriter().use { writer ->
lines.forEach { writer.println(it) }
}
// pkill not available on Lollipop. Source: https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md
RootSession.use {
it.exec("cat ${tempFile.absolutePath} > ${if (legacy) CONF_PATH_LEGACY else CONF_PATH_TREBLE}")
if (Build.VERSION.SDK_INT >= 23) it.exec("pkill wpa_supplicant") else {
val result = try {
it.execOut("ps | grep wpa_supplicant").split(whitespaceMatcher).apply { check(size >= 2) }
} catch (e: Exception) {
throw IllegalStateException("wpa_supplicant not found, please toggle Airplane mode manually", e)
}
it.exec("kill ${result[1]}")
}
}
} finally {
if (!tempFile.delete()) tempFile.deleteOnExit()
}
RootManager.use { it.execute(RepeaterCommands.WriteP2pConfig(lines.joinToString("\n"), legacy)) }
}
}

View File

@@ -38,10 +38,10 @@ data class SoftApConfigurationCompat(
/**
* TODO
*/
const val BAND_ANY = -1
const val BAND_2GHZ = 0
const val BAND_5GHZ = 1
const val BAND_6GHZ = 2
const val BAND_ANY = 0
const val BAND_2GHZ = 1
const val BAND_5GHZ = 2
const val BAND_6GHZ = 3
const val CH_INVALID = 0
// TODO: localize?
@@ -144,7 +144,9 @@ data class SoftApConfigurationCompat(
classBuilder.getDeclaredMethod("setBssid", MacAddress::class.java)
}
@get:RequiresApi(30)
private val setChannel by lazy { classBuilder.getDeclaredMethod("setChannel", Int::class.java) }
private val setChannel by lazy {
classBuilder.getDeclaredMethod("setChannel", Int::class.java, Int::class.java)
}
@get:RequiresApi(30)
private val setClientControlByUserEnabled by lazy {
classBuilder.getDeclaredMethod("setClientControlByUserEnabled", Boolean::class.java)
@@ -156,7 +158,9 @@ data class SoftApConfigurationCompat(
classBuilder.getDeclaredMethod("setMaxNumberOfClients", Int::class.java)
}
@get:RequiresApi(30)
private val setPassphrase by lazy { classBuilder.getDeclaredMethod("setPassphrase", String::class.java) }
private val setPassphrase by lazy {
classBuilder.getDeclaredMethod("setPassphrase", String::class.java, Int::class.java)
}
@get:RequiresApi(30)
private val setShutdownTimeoutMillis by lazy {
classBuilder.getDeclaredMethod("setShutdownTimeoutMillis", Long::class.java)
@@ -186,7 +190,7 @@ data class SoftApConfigurationCompat(
}
},
preSharedKey,
if (Build.VERSION.SDK_INT >= 23) apBand.getInt(this) else BAND_ANY, // TODO
if (Build.VERSION.SDK_INT >= 23) apBand.getInt(this) + 1 else BAND_ANY, // TODO
if (Build.VERSION.SDK_INT >= 23) apChannel.getInt(this) else CH_INVALID, // TODO
BSSID?.let { MacAddressCompat.fromString(it) }?.addr,
0, // TODO: unsupported field should have @RequiresApi?
@@ -275,10 +279,10 @@ data class SoftApConfigurationCompat(
// TODO: can we always call copy constructor?
val builder = if (sac == null) classBuilder.newInstance() else newBuilder.newInstance(sac)
setSsid(builder, ssid)
// TODO: setSecurityType
setPassphrase(builder, passphrase)
setBand(builder, band)
setChannel(builder, channel)
setPassphrase(builder, passphrase, securityType)
// TODO: how to use these?
// setBand(builder, band)
// setChannel(builder, band, channel)
setBssid(builder, bssid?.toPlatform())
setMaxNumberOfClients(builder, maxNumberOfClients)
setShutdownTimeoutMillis(builder, shutdownTimeoutMillis)

View File

@@ -5,8 +5,8 @@ import android.net.wifi.SoftApConfiguration
import android.net.wifi.WifiManager
import android.os.Build
import androidx.annotation.RequiresApi
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat
import be.mygod.vpnhotspot.util.Services
object WifiApManager {
private val getWifiApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getWifiApConfiguration") }
@@ -22,22 +22,18 @@ object WifiApManager {
WifiManager::class.java.getDeclaredMethod("setSoftApConfiguration", SoftApConfiguration::class.java)
}
var configuration: SoftApConfigurationCompat
get() = if (Build.VERSION.SDK_INT < 30) @Suppress("DEPRECATION") {
(getWifiApConfiguration(app.wifi) as android.net.wifi.WifiConfiguration?)?.toCompat()
?: SoftApConfigurationCompat.empty()
} else (getSoftApConfiguration(app.wifi) as SoftApConfiguration).toCompat()
set(value) = if (Build.VERSION.SDK_INT < 30) @Suppress("DEPRECATION") {
require(setWifiApConfiguration(app.wifi,
value.toWifiConfiguration()) as Boolean) { "setWifiApConfiguration failed" }
} else require(setSoftApConfiguration(app.wifi, value.toPlatform()) as Boolean) {
"setSoftApConfiguration failed"
}
val configuration get() = if (Build.VERSION.SDK_INT < 30) @Suppress("DEPRECATION") {
(getWifiApConfiguration(Services.wifi) as android.net.wifi.WifiConfiguration?)?.toCompat()
?: SoftApConfigurationCompat.empty()
} else (getSoftApConfiguration(Services.wifi) as SoftApConfiguration).toCompat()
fun setConfiguration(value: SoftApConfigurationCompat) = (if (Build.VERSION.SDK_INT < 30) @Suppress("DEPRECATION") {
setWifiApConfiguration(Services.wifi, value.toWifiConfiguration())
} else setSoftApConfiguration(Services.wifi, value.toPlatform())) as Boolean
private val cancelLocalOnlyHotspotRequest by lazy {
WifiManager::class.java.getDeclaredMethod("cancelLocalOnlyHotspotRequest")
}
fun cancelLocalOnlyHotspotRequest() = cancelLocalOnlyHotspotRequest(app.wifi)
fun cancelLocalOnlyHotspotRequest() = cancelLocalOnlyHotspotRequest(Services.wifi)
@Suppress("DEPRECATION")
private val setWifiApEnabled by lazy {
@@ -66,13 +62,13 @@ object WifiApManager {
@Suppress("DEPRECATION")
@Deprecated("Not usable since API 26, malfunctioning on API 25")
fun start(wifiConfig: android.net.wifi.WifiConfiguration? = null) {
app.wifi.isWifiEnabled = false
app.wifi.setWifiApEnabled(wifiConfig, true)
Services.wifi.isWifiEnabled = false
Services.wifi.setWifiApEnabled(wifiConfig, true)
}
@Suppress("DEPRECATION")
@Deprecated("Not usable since API 26")
fun stop() {
app.wifi.setWifiApEnabled(null, false)
app.wifi.isWifiEnabled = true
Services.wifi.setWifiApEnabled(null, false)
Services.wifi.isWifiEnabled = true
}
}

View File

@@ -13,6 +13,7 @@ import androidx.core.content.getSystemService
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.util.Services
/**
* This mechanism is used to maximize profit. Source: https://stackoverflow.com/a/29657230/2245107
@@ -91,7 +92,7 @@ class WifiDoubleLock(lockType: Int) : AutoCloseable {
override fun onDestroy(owner: LifecycleOwner) = app.pref.unregisterOnSharedPreferenceChangeListener(this)
}
private val wifi = app.wifi.createWifiLock(lockType, "vpnhotspot:wifi").apply { acquire() }
private val wifi = Services.wifi.createWifiLock(lockType, "vpnhotspot:wifi").apply { acquire() }
@SuppressLint("WakelockTimeout")
private val power = service.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "vpnhotspot:power").apply { acquire() }