From bf79573b271b900400e75db0fcb409079268c769 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 14 Jun 2020 07:56:08 +0800 Subject: [PATCH] Misc BSSID fixes --- README.md | 1 + mobile/src/main/AndroidManifest.xml | 2 + .../be/mygod/vpnhotspot/RepeaterService.kt | 38 ++-- .../vpnhotspot/manage/RepeaterManager.kt | 3 +- .../mygod/vpnhotspot/net/MacAddressCompat.kt | 2 +- .../net/wifi/P2pSupplicantConfiguration.kt | 163 +++++++++--------- 6 files changed, 113 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index 05b56251..01382a89 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 28) [`Lcom/android/internal/R$integer;->config_wifi_framework_soft_ap_timeout_delay:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#245007) * [`Lcom/android/internal/R$string;->config_ethernet_iface_regex:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#245611) * (since API 27) `Lcom/android/server/connectivity/tethering/OffloadHardwareInterface;->DEFAULT_TETHER_OFFLOAD_DISABLED:I` +* (since API 29) `Lcom/android/server/wifi/p2p/WifiP2pServiceImpl;->ANONYMIZED_DEVICE_ADDRESS:Ljava/lang/String;` * (since API 30) `Lcom/android/server/SystemServer;->TETHERING_CONNECTOR_CLASS:Ljava/lang/String;` * (since API 26) [`Ljava/lang/invoke/MethodHandles$Lookup;->(Ljava/lang/Class;I)V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#333141) * (since API 26) [`Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,greylist-max-o`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#333142) diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 344db496..c922ce56 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -32,6 +32,8 @@ + when (intent.action) { - WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> binder.thisDevice = - intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE) + WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> { + val device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE) + val address = MacAddressCompat.fromString(device?.deviceAddress ?: return@broadcastReceiver) + if (Build.VERSION.SDK_INT < 29 || address != MacAddressCompat.ANY_ADDRESS) { + lastMac = device.deviceAddress + } + } WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION -> if (!safeMode) onPersistentGroupsChanged() } } @@ -242,13 +246,25 @@ class RepeaterService : Service(), CoroutineScope, WifiP2pManager.ChannelListene } @SuppressLint("NewApi") // networkId is available since Android 4.2 - private fun onPersistentGroupsChanged() { - val channel = channel ?: return - val device = binder.thisDevice ?: return + private fun onPersistentGroupsChanged() = launch { + val ownerAddress = lastMac?.let(MacAddressCompat.Companion::fromString) ?: withContext(Dispatchers.Default) { + try { + P2pSupplicantConfiguration().bssid + } catch (e: RuntimeException) { + Timber.d(e) + null + } + } ?: return@launch + val channel = channel ?: return@launch try { - p2pManager.requestPersistentGroupInfo(channel) { - if (it.isNotEmpty()) persistentSupported = true - val ownedGroups = it.filter { it.isGroupOwner && it.owner.deviceAddress == device.deviceAddress } + p2pManager.requestPersistentGroupInfo(channel) { groups -> + if (groups.isNotEmpty()) persistentSupported = true + val ownedGroups = groups.filter { + if (!it.isGroupOwner) return@filter false + val address = MacAddressCompat.fromString(it.owner.deviceAddress) + // WifiP2pServiceImpl only removes self address + Build.VERSION.SDK_INT >= 29 && address == MacAddressCompat.ANY_ADDRESS || address == ownerAddress + } val main = ownedGroups.minBy { it.networkId } // do not replace current group if it's better if (binder.group?.passphrase == null) binder.group = main diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index 23a07563..c68b9678 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -199,8 +199,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic val group = binder?.group if (group != null) try { val config = withContext(Dispatchers.Default) { - P2pSupplicantConfiguration(group, - binder?.thisDevice?.deviceAddress, RepeaterService.deviceAddress?.toString()) + P2pSupplicantConfiguration(group, RepeaterService.lastMac) } holder.config = config return SoftApConfigurationCompat.empty().apply { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt index 5894ed22..1570fcb1 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt @@ -18,7 +18,7 @@ inline class MacAddressCompat(val addr: Long) { * @hide */ val ALL_ZEROS_ADDRESS = MacAddressCompat(0) - val ANY_ADDRESS = MacAddressCompat(2L shl 40) + val ANY_ADDRESS = MacAddressCompat(2) fun bytesToString(addr: ByteArray): String { require(addr.size == ETHER_ADDR_LEN) { addr.contentToString() + " was not a valid MAC address" } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt index b50a8322..0233147a 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/P2pSupplicantConfiguration.kt @@ -14,7 +14,7 @@ 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/+/6fa46df/wpa_supplicant/config_file.c#182 */ -class P2pSupplicantConfiguration(private val group: WifiP2pGroup?, vararg ownerAddresses: String?) { +class P2pSupplicantConfiguration(private val group: WifiP2pGroup? = null, ownerAddress: String? = null) { companion object { private const val TAG = "P2pSupplicantConfiguration" private const val CONF_PATH_TREBLE = "/data/vendor/wifi/wpa/p2p_supplicant.conf" @@ -49,96 +49,95 @@ class P2pSupplicantConfiguration(private val group: WifiP2pGroup?, vararg ownerA private data class Content(val lines: ArrayList, var target: NetworkBlock, var persistentMacLine: Int?, var legacy: Boolean) - private val content by lazy { - RootSession.use { - val result = ArrayList() - 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) - try { - var bssids = (listOf(group?.owner?.deviceAddress, RepeaterService.lastMac) + ownerAddresses) - .filterNotNull() - .distinct() - .filter { - try { - val mac = MacAddressCompat.fromString(it) - mac != MacAddressCompat.ALL_ZEROS_ADDRESS && mac != MacAddressCompat.ANY_ADDRESS - } catch (_: IllegalArgumentException) { - false - } + private val content = RootSession.use { + val result = ArrayList() + 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) + try { + var bssids = listOfNotNull(group?.owner?.deviceAddress, ownerAddress) + .distinct() + .filter { + try { + val mac = MacAddressCompat.fromString(it) + mac != MacAddressCompat.ALL_ZEROS_ADDRESS && mac != MacAddressCompat.ANY_ADDRESS + } catch (_: IllegalArgumentException) { + false } - while (parser.next()) { - if (parser.trimmed.startsWith("network={")) { - val block = NetworkBlock() - block.add(parser.line) - while (parser.next() && !parser.trimmed.startsWith('}')) { - if (parser.trimmed.startsWith("ssid=")) { - check(block.ssidLine == null) - block.ssidLine = block.size - } else if (parser.trimmed.startsWith("mode=3")) block.groupOwner = true else { - val match = networkParser.find(parser.trimmed) - if (match != null) match.groupValues[2].also { matchedBssid -> - if (matchedBssid.isEmpty()) { - check(block.pskLine == null && block.psk == null) - if (match.groups[5] != null) { - block.psk = match.groupValues[5].apply { check(length in 8..63) } - } - block.pskLine = block.size - } else if (bssids.any { matchedBssid.equals(it, true) }) { - block.bssid = matchedBssid - block.bssidLine = block.size + } + while (parser.next()) { + if (parser.trimmed.startsWith("network={")) { + val block = NetworkBlock() + block.add(parser.line) + while (parser.next() && !parser.trimmed.startsWith('}')) { + if (parser.trimmed.startsWith("ssid=")) { + check(block.ssidLine == null) + block.ssidLine = block.size + } else if (parser.trimmed.startsWith("mode=3")) block.groupOwner = true else { + val match = networkParser.find(parser.trimmed) + if (match != null) match.groupValues[2].also { matchedBssid -> + if (matchedBssid.isEmpty()) { + check(block.pskLine == null && block.psk == null) + if (match.groups[5] != null) { + block.psk = match.groupValues[5].apply { check(length in 8..63) } } + block.pskLine = block.size + } else if (bssids.any { matchedBssid.equals(it, true) }) { + block.bssid = matchedBssid + block.bssidLine = block.size } } - block.add(parser.line) } block.add(parser.line) - result.add(block) - if (block.bssid != null && block.groupOwner && target == null) { // keep first only - check(block.ssidLine != null && block.pskLine != null) - target = block - } - } else { - if (parser.trimmed.startsWith(PERSISTENT_MAC)) { - require(persistentMacLine == null) { "Duplicated $PERSISTENT_MAC" } - persistentMacLine = result.size - bssids = listOf(parser.trimmed.substring(PERSISTENT_MAC.length)) - } - result.add(parser.line) } + block.add(parser.line) + result.add(block) + if (block.bssid != null && block.groupOwner && target == null) { // keep first only + check(block.ssidLine != null && block.pskLine != null) + target = block + } + } else { + if (parser.trimmed.startsWith(PERSISTENT_MAC)) { + require(persistentMacLine == null) { "Duplicated $PERSISTENT_MAC" } + persistentMacLine = result.size + bssids = listOf(parser.trimmed.substring(PERSISTENT_MAC.length)) + } + result.add(parser.line) } - if (target == null && !RepeaterService.persistentSupported) { - result.add("") - result.add(NetworkBlock().apply { - // generate a basic network block, it is likely that vendor is going to add more stuff here - add("network={") - ssidLine = size - add("") - add("\tbssid=$bssid") - pskLine = size - add("") - add("\tproto=RSN") - add("\tkey_mgmt=WPA-PSK") - add("\tpairwise=CCMP") - add("\tauth_alg=OPEN") - add("\tmode=3") - add("\tdisabled=2") - add("}") - if (target == null) target = this - }) - } - Content(result, target!!, persistentMacLine, shell.err.isNotEmpty()) - } catch (e: RuntimeException) { - FirebaseCrashlytics.getInstance().apply { - setCustomKey(TAG, parser.lines.joinToString("\n")) - setCustomKey("$TAG.ownerAddresses", ownerAddresses.joinToString()) - setCustomKey("$TAG.p2pGroup", group.toString()) - } - throw e } + if (target == null && !RepeaterService.persistentSupported) { + result.add("") + result.add(NetworkBlock().apply { + // generate a basic network block, it is likely that vendor is going to add more stuff here + add("network={") + ssidLine = size + add("") + add("\tbssid=$bssid") + pskLine = size + add("") + add("\tproto=RSN") + add("\tkey_mgmt=WPA-PSK") + add("\tpairwise=CCMP") + add("\tauth_alg=OPEN") + add("\tmode=3") + add("\tdisabled=2") + add("}") + if (target == null) target = this + }) + } + Content(result, target!!.apply { + RepeaterService.lastMac = bssid!! + }, persistentMacLine, shell.err.isNotEmpty()) + } catch (e: RuntimeException) { + FirebaseCrashlytics.getInstance().apply { + setCustomKey(TAG, parser.lines.joinToString("\n")) + setCustomKey("$TAG.ownerAddress", ownerAddress.toString()) + setCustomKey("$TAG.p2pGroup", group.toString()) + } + throw e } } val psk = group?.passphrase ?: content.target.psk!!