Correctly implement bridged AP, third attempt

This commit is contained in:
Mygod
2021-10-08 14:17:34 -04:00
parent bef36d0889
commit da7d318f60
8 changed files with 79 additions and 207 deletions

View File

@@ -24,11 +24,9 @@ data class SoftApConfigurationCompat(
var passphrase: String? = null,
var isHiddenSsid: Boolean = false,
/**
* To read legacy band/channel pair, use [requireSingleBand]. For easy access, see [getChannel].
*
* You should probably set or modify this field directly only when you want to use bridged AP,
* see also [android.net.wifi.WifiManager.isBridgedApConcurrencySupported].
* Otherwise, use [optimizeChannels] or [setChannel].
* Otherwise, use [requireSingleBand] and [setChannel].
*/
@TargetApi(23)
var channels: SparseIntArray = SparseIntArray(1).apply { append(BAND_2GHZ, 0) },
@@ -62,6 +60,10 @@ data class SoftApConfigurationCompat(
@TargetApi(31)
const val BAND_60GHZ = 8
const val BAND_LEGACY = BAND_2GHZ or BAND_5GHZ
@TargetApi(30)
const val BAND_ANY_30 = BAND_LEGACY or BAND_6GHZ
@TargetApi(31)
const val BAND_ANY_31 = BAND_ANY_30 or BAND_60GHZ
val BAND_TYPES by lazy {
if (Build.VERSION.SDK_INT >= 31) try {
return@lazy UnblockCentral.SoftApConfiguration_BAND_TYPES
@@ -341,15 +343,6 @@ data class SoftApConfigurationCompat(
require(channels.size() == 1) { "Unsupported number of bands configured" }
return channels.keyAt(0) to channels.valueAt(0)
}
fun optimizeChannels(channels: SparseIntArray) = SparseIntArray(channels.size()).apply {
var setBand = 0
repeat(channels.size()) { i -> if (channels.valueAt(i) == 0) setBand = setBand or channels.keyAt(i) }
if (setBand != 0) append(setBand, 0) // merge all bands into one
repeat(channels.size()) { i ->
val band = channels.keyAt(i)
if (band and setBand == 0) put(band, channels.valueAt(i))
}
}
@RequiresApi(30)
private fun setChannelsCompat(builder: Any, channels: SparseIntArray) = if (Build.VERSION.SDK_INT < 31) {
@@ -369,15 +362,6 @@ data class SoftApConfigurationCompat(
bssidAddr = value?.addr
}
fun getChannel(band: Int): Int {
var result = -1
repeat(channels.size()) { i ->
if (band and channels.keyAt(i) != band) return@repeat
require(result == -1) { "Duplicate band found" }
result = channels.valueAt(i)
}
return result
}
fun setChannel(channel: Int, band: Int = BAND_LEGACY) {
channels = SparseIntArray(1).apply {
append(when {

View File

@@ -36,6 +36,8 @@ import be.mygod.vpnhotspot.util.readableMessage
import be.mygod.vpnhotspot.util.showAllowingStateLoss
import kotlinx.parcelize.Parcelize
import timber.log.Timber
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
/**
* Based on: https://android.googlesource.com/platform/packages/apps/Settings/+/39b4674/src/com/android/settings/wifi/WifiApDialog.java
@@ -48,26 +50,30 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
companion object {
private const val BASE64_FLAGS = Base64.NO_PADDING or Base64.NO_WRAP
private val nonMacChars = "[^0-9a-fA-F:]+".toRegex()
private val baseOptions by lazy { listOf(ChannelOption.Disabled, ChannelOption.Auto) }
private val channels2G by lazy {
baseOptions + (1..14).map { ChannelOption(it, SoftApConfigurationCompat.BAND_2GHZ) }
}
private val channels2G = (1..14).map { ChannelOption(SoftApConfigurationCompat.BAND_2GHZ, it) }
private val channels5G by lazy {
baseOptions + (1..196).map { ChannelOption(it, SoftApConfigurationCompat.BAND_5GHZ) }
}
@get:RequiresApi(30)
private val channels6G by lazy {
baseOptions + (1..233).map { ChannelOption(it, SoftApConfigurationCompat.BAND_6GHZ) }
}
@get:RequiresApi(31)
private val channels60G by lazy {
baseOptions + (1..6).map { ChannelOption(it, SoftApConfigurationCompat.BAND_60GHZ) }
channels2G + (1..196).map { ChannelOption(SoftApConfigurationCompat.BAND_5GHZ, it) }
}
private fun genAutoOptions(band: Int) = (1..band).filter { it and band == it }.map { ChannelOption(it) }
/**
* Source: https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/c2fc6a1/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java#1396
*/
private val p2pChannels by lazy {
baseOptions + (15..165).map { ChannelOption(it, SoftApConfigurationCompat.BAND_5GHZ) }
private val p2pUnsafeOptions by lazy {
listOf(ChannelOption(SoftApConfigurationCompat.BAND_LEGACY)) +
channels2G + (15..165).map { ChannelOption(SoftApConfigurationCompat.BAND_5GHZ, it) }
}
/**
* Also used for legacy soft ap.
*/
private val p2pSafeOptions by lazy { genAutoOptions(SoftApConfigurationCompat.BAND_LEGACY) + channels5G }
@get:RequiresApi(30)
private val softApOptions by lazy {
val channels6G = channels5G + (1..233).map { ChannelOption(SoftApConfigurationCompat.BAND_6GHZ, it) }
if (Build.VERSION.SDK_INT >= 31) {
genAutoOptions(SoftApConfigurationCompat.BAND_ANY_31) +
channels6G + (1..6).map { ChannelOption(SoftApConfigurationCompat.BAND_60GHZ, it) }
} else genAutoOptions(SoftApConfigurationCompat.BAND_ANY_30) + channels6G
}
}
@@ -80,34 +86,36 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
*/
val p2pMode: Boolean = false) : Parcelable
private open class ChannelOption(val channel: Int = 0, private val band: Int = 0) {
private open class ChannelOption(val band: Int = 0, val channel: Int = 0) {
object Disabled : ChannelOption(-1) {
override fun toString() = app.getString(R.string.wifi_ap_choose_disabled)
}
object Auto : ChannelOption() {
override fun toString() = app.getString(R.string.wifi_ap_choose_auto)
}
override fun toString() = "${SoftApConfigurationCompat.channelToFrequency(band, channel)} MHz ($channel)"
override fun toString() = if (channel == 0) {
val format = DecimalFormat("#.#", DecimalFormatSymbols.getInstance(app.resources.configuration.locale))
app.getString(R.string.wifi_ap_choose_G, arrayOf(
SoftApConfigurationCompat.BAND_2GHZ to 2.4,
SoftApConfigurationCompat.BAND_5GHZ to 5,
SoftApConfigurationCompat.BAND_6GHZ to 6,
SoftApConfigurationCompat.BAND_60GHZ to 60,
).filter { (mask, _) -> band and mask == mask }.joinToString("/") { (_, name) -> format.format(name) })
} else "${SoftApConfigurationCompat.channelToFrequency(band, channel)} MHz ($channel)"
}
private lateinit var dialogView: DialogWifiApBinding
private lateinit var base: SoftApConfigurationCompat
private var started = false
private val currentChannels5G get() = if (arg.p2pMode && !RepeaterService.safeMode) p2pChannels else channels5G
private val currentChannels get() = when {
arg.p2pMode && !RepeaterService.safeMode -> p2pUnsafeOptions
arg.p2pMode || Build.VERSION.SDK_INT < 30 -> p2pSafeOptions
else -> softApOptions
}
override val ret get() = Arg(generateConfig())
private fun generateChannels() = SparseIntArray(4).apply {
for ((band, spinner) in arrayOf(SoftApConfigurationCompat.BAND_2GHZ to dialogView.band2G,
SoftApConfigurationCompat.BAND_5GHZ to dialogView.band5G,
SoftApConfigurationCompat.BAND_6GHZ to dialogView.band6G,
SoftApConfigurationCompat.BAND_60GHZ to dialogView.band60G)) {
val channel = (spinner.selectedItem as ChannelOption?)?.channel
if (channel != null && channel >= 0) append(band, channel)
private fun generateChannels() = SparseIntArray(2).apply {
if (!arg.p2pMode && Build.VERSION.SDK_INT >= 31) {
(dialogView.bandSecondary.selectedItem as ChannelOption?)?.apply { if (band >= 0) put(band, channel) }
}
}.let {
if (arg.p2pMode || Build.VERSION.SDK_INT < 31 || !dialogView.bridgedMode.isChecked || it.size() > 2) {
SoftApConfigurationCompat.optimizeChannels(it)
} else it
(dialogView.bandPrimary.selectedItem as ChannelOption).apply { put(band, channel) }
}
private fun generateConfig(full: Boolean = true) = base.copy(
ssid = dialogView.ssid.text.toString(),
@@ -188,15 +196,10 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
if (!arg.readOnly) onItemSelectedListener = this@WifiApDialogFragment
}
if (Build.VERSION.SDK_INT >= 23 || arg.p2pMode) {
dialogView.band2G.configure(channels2G)
dialogView.band5G.configure(currentChannels5G)
if (Build.VERSION.SDK_INT >= 30 && !arg.p2pMode) dialogView.band6G.configure(channels6G)
else dialogView.bandWrapper6G.isGone = true
if (Build.VERSION.SDK_INT >= 31 && !arg.p2pMode) dialogView.band60G.configure(channels60G) else {
dialogView.bandWrapper60G.isGone = true
dialogView.bridgedMode.isGone = true
dialogView.bridgedModeOpportunisticShutdown.isGone = true
}
dialogView.bandPrimary.configure(currentChannels)
if (Build.VERSION.SDK_INT >= 31 && !arg.p2pMode) {
dialogView.bandSecondary.configure(listOf(ChannelOption.Disabled) + currentChannels)
} else dialogView.bandSecondary.isGone = true
} else dialogView.bandGroup.isGone = true
if (arg.p2pMode || Build.VERSION.SDK_INT < 30) dialogView.accessControlGroup.isGone = true
else if (!arg.readOnly) {
@@ -210,41 +213,22 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
else if (arg.p2pMode || Build.VERSION.SDK_INT < 31) dialogView.macRandomization.isGone = true
if (arg.p2pMode || Build.VERSION.SDK_INT < 31) {
dialogView.ieee80211ax.isGone = true
dialogView.bridgedModeOpportunisticShutdown.isGone = true
dialogView.userConfig.isGone = true
}
base = arg.configuration
populateFromConfiguration()
}
private fun locate(band: Int, channels: List<ChannelOption>): Int {
val channel = base.getChannel(band)
val selection = channels.indexOfFirst { it.channel == channel }
private fun locate(i: Int): Int {
val band = base.channels.keyAt(i)
val channel = base.channels.valueAt(i)
val selection = currentChannels.indexOfFirst { it.band == band && it.channel == channel }
return if (selection == -1) {
Timber.w(Exception("Unable to locate $band, $channel, ${arg.p2pMode && !RepeaterService.safeMode}"))
0
} else selection
}
private var userBridgedMode = false
private fun setBridgedMode(): Boolean {
var auto = 0
var set = 0
for (s in arrayOf(dialogView.band2G, dialogView.band5G, dialogView.band6G,
dialogView.band60G)) when (s.selectedItem) {
is ChannelOption.Auto -> auto = 1
!is ChannelOption.Disabled -> ++set
}
if (auto + set > 1) {
if (dialogView.bridgedMode.isEnabled) {
userBridgedMode = dialogView.bridgedMode.isChecked
dialogView.bridgedMode.isEnabled = false
dialogView.bridgedMode.isChecked = true
}
} else if (!dialogView.bridgedMode.isEnabled) {
dialogView.bridgedMode.isEnabled = true
dialogView.bridgedMode.isChecked = userBridgedMode
}
return auto + set > 0
}
private fun populateFromConfiguration() {
dialogView.ssid.setText(base.ssid)
if (!arg.p2pMode) dialogView.security.setSelection(base.securityType)
@@ -252,13 +236,10 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
dialogView.autoShutdown.isChecked = base.isAutoShutdownEnabled
dialogView.timeout.setText(base.shutdownTimeoutMillis.let { if (it == 0L) "" else it.toString() })
if (Build.VERSION.SDK_INT >= 23 || arg.p2pMode) {
dialogView.band2G.setSelection(locate(SoftApConfigurationCompat.BAND_2GHZ, channels2G))
dialogView.band5G.setSelection(locate(SoftApConfigurationCompat.BAND_5GHZ, currentChannels5G))
dialogView.band6G.setSelection(locate(SoftApConfigurationCompat.BAND_6GHZ, channels6G))
dialogView.band60G.setSelection(locate(SoftApConfigurationCompat.BAND_60GHZ, channels60G))
userBridgedMode = base.channels.size() > 1
dialogView.bridgedMode.isChecked = userBridgedMode
setBridgedMode()
dialogView.bandPrimary.setSelection(locate(0))
if (Build.VERSION.SDK_INT >= 31 && !arg.p2pMode) {
dialogView.bandSecondary.setSelection(if (base.channels.size() > 1) locate(1) + 1 else 0)
}
}
dialogView.bssid.setText(base.bssid?.toString())
dialogView.hiddenSsid.isChecked = base.isHiddenSsid
@@ -308,26 +289,14 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
}
}
dialogView.timeoutWrapper.error = timeoutError
val bandError = if (arg.p2pMode || Build.VERSION.SDK_INT < 30) {
val option5G = dialogView.band5G.selectedItem
val valid = when (dialogView.band2G.selectedItem) {
is ChannelOption.Disabled -> option5G !is ChannelOption.Disabled &&
(!arg.p2pMode || RepeaterService.safeMode || option5G !is ChannelOption.Auto)
is ChannelOption.Auto ->
(arg.p2pMode || Build.VERSION.SDK_INT >= 28) && option5G is ChannelOption.Auto ||
(!arg.p2pMode || RepeaterService.safeMode) && option5G is ChannelOption.Disabled
else -> option5G is ChannelOption.Disabled
}
if (valid) null else ""
} else {
if (Build.VERSION.SDK_INT >= 31) setBridgedMode()
val bandError = if (!arg.p2pMode && Build.VERSION.SDK_INT >= 30) {
try {
SoftApConfigurationCompat.testPlatformValidity(generateChannels())
null
} catch (e: Exception) {
e.readableMessage
}
}
} else null
dialogView.bandError.isGone = bandError.isNullOrEmpty()
dialogView.bandError.text = bandError
dialogView.bssidWrapper.error = null

View File

@@ -128,88 +128,27 @@
android:id="@+id/band_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:orientation="vertical">
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/wifi_item_divider" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/wifi_item_subhead"
style="@style/wifi_item_label"
android:text="@string/wifi_hotspot_ap_band_title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
style="@style/wifi_item_label"
android:text="@string/wifi_ap_choose_2G" />
<Spinner
android:id="@+id/band_2G"
android:id="@+id/band_primary"
style="@style/wifi_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/touch_target_min"
android:prompt="@string/wifi_ap_choose_2G" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
style="@style/wifi_item_label"
android:text="@string/wifi_ap_choose_5G" />
android:prompt="@string/wifi_hotspot_ap_band_title" />
<Spinner
android:id="@+id/band_5G"
android:id="@+id/band_secondary"
style="@style/wifi_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/touch_target_min"
android:prompt="@string/wifi_ap_choose_5G" />
<LinearLayout
android:id="@+id/band_wrapper_6G"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/wifi_item_label"
android:text="@string/wifi_ap_choose_6G" />
<Spinner
android:id="@+id/band_6G"
style="@style/wifi_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/touch_target_min"
android:prompt="@string/wifi_ap_choose_6G" />
</LinearLayout>
<LinearLayout
android:id="@+id/band_wrapper_60G"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/wifi_item_label"
android:text="@string/wifi_ap_choose_60G" />
<Spinner
android:id="@+id/band_60G"
style="@style/wifi_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/touch_target_min"
android:prompt="@string/wifi_ap_choose_60G" />
</LinearLayout>
<Switch
android:id="@+id/bridged_mode"
style="@style/wifi_item_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:minHeight="@dimen/touch_target_min"
android:text="@string/wifi_bridged_mode" />
android:prompt="@string/wifi_hotspot_ap_band_title" />
<TextView
android:id="@+id/band_error"
android:layout_width="match_parent"
@@ -220,14 +159,6 @@
android:visibility="gone"
tools:text="error text placeholder"
tools:visibility="visible"/>
<Switch
android:id="@+id/bridged_mode_opportunistic_shutdown"
style="@style/wifi_item_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:minHeight="@dimen/touch_target_min"
android:text="@string/wifi_bridged_mode_opportunistic_shutdown" />
</LinearLayout>
<LinearLayout
@@ -354,6 +285,14 @@
android:layout_marginTop="8dip"
android:minHeight="@dimen/touch_target_min"
android:text="@string/wifi_ieee_80211ax" />
<Switch
android:id="@+id/bridged_mode_opportunistic_shutdown"
style="@style/wifi_item_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:minHeight="@dimen/touch_target_min"
android:text="@string/wifi_bridged_mode_opportunistic_shutdown" />
<Switch
android:id="@+id/user_config"
style="@style/wifi_item_label"

View File

@@ -154,9 +154,7 @@
<string name="wifi_password">Password</string>
<string name="wifi_hotspot_auto_off" msgid="5858098059725925084">"L\'hotspot WiFi viene disattivato se non ci sono dispositivi collegati"</string>
<string name="wifi_hotspot_ap_band_title">Banda AP</string>
<string name="wifi_ap_choose_auto" msgid="2677800651271769965">"Automatica"</string>
<string name="wifi_ap_choose_2G" msgid="8724267386885036210">"Banda a 2,4 GHz"</string>
<string name="wifi_ap_choose_5G" msgid="8813128641914385634">"Banda a 5 GHz"</string>
<string name="wifi_ap_choose_G" msgid="8724267386885036210">"Banda a %s GHz"</string>
<string name="wifi_advanced_mac_address_title" msgid="6571335466330978393">"Indirizzo MAC"</string>
<string name="wifi_hidden_network" msgid="973162091800925000">"Rete nascosta"</string>
<string name="wifi_save">Salva</string>

View File

@@ -59,11 +59,7 @@
<string name="wifi_password" msgid="5948219759936151048">"Пароль"</string>
<string name="wifi_hotspot_auto_off" msgid="5858098059725925084">"Выключать точку доступа WiFi автоматически, если к ней не подключено ни одного устройства"</string>
<string name="wifi_hotspot_ap_band_title" msgid="1165801173359290681">"Диапазон частот Wi-Fi"</string>
<string name="wifi_ap_choose_auto" msgid="2677800651271769965">"Авто"</string>
<string name="wifi_ap_choose_2G" msgid="8724267386885036210">"2,4 ГГц"</string>
<string name="wifi_ap_choose_5G" msgid="8813128641914385634">"5 ГГц"</string>
<string name="wifi_ap_choose_6G">"6 ГГц"</string>
<string name="wifi_ap_choose_60G">"60 ГГц"</string>
<string name="wifi_ap_choose_G" msgid="8724267386885036210">"%s ГГц"</string>
<string name="wifi_advanced_mac_address_title" msgid="6571335466330978393">"MAC-адрес"</string>
<string name="wifi_hidden_network" msgid="973162091800925000">"Скрытая сеть"</string>
<string name="wifi_save" msgid="3331121567988522826">"Сохранить"</string>

View File

@@ -180,11 +180,7 @@
<string name="wifi_hotspot_timeout_default">默认延迟:%d 毫秒</string>
<string name="wifi_hotspot_ap_band_title" msgid="1165801173359290681">"AP 频段"</string>
<string name="wifi_ap_choose_disabled">Disabled</string>
<string name="wifi_ap_choose_auto" msgid="2677800651271769965">"自动"</string>
<string name="wifi_ap_choose_2G" msgid="8724267386885036210">"2.4 GHz 频段"</string>
<string name="wifi_ap_choose_5G" msgid="8813128641914385634">"5 GHz 频段"</string>
<string name="wifi_ap_choose_6G">6 GHz 频段</string>
<string name="wifi_ap_choose_60G">60 GHz 频段</string>
<string name="wifi_ap_choose_G" msgid="8724267386885036210">"%s GHz 频段"</string>
<string name="wifi_hotspot_access_control_title">访问控制</string>
<string name="wifi_hotspot_ap_advanced_title">高级接入点设置</string>
<string name="wifi_advanced_mac_address_title" msgid="6571335466330978393">"MAC 地址"</string>
@@ -194,7 +190,6 @@
<string name="wifi_blocked_list">设备黑名单</string>
<string name="wifi_allowed_list">设备白名单</string>
<string name="wifi_mac_randomization">随机生成 MAC 地址</string>
<string name="wifi_bridged_mode">启用无线接入点桥接模式</string>
<string name="wifi_bridged_mode_opportunistic_shutdown">启用桥接模式伺机关闭</string>
<string name="wifi_ieee_80211ax">启用 Wi\u2011Fi 6</string>
<string name="wifi_user_config">用户提供配置</string>

View File

@@ -177,11 +177,7 @@
<string name="wifi_hotspot_timeout_default">默認延遲:%d 毫秒</string>
<string name="wifi_hotspot_ap_band_title" msgid="1165801173359290681">AP 頻帶</string>
<string name="wifi_ap_choose_disabled">停用</string>
<string name="wifi_ap_choose_auto" msgid="2677800651271769965">自動</string>
<string name="wifi_ap_choose_2G" msgid="8724267386885036210">2.4 GHz 頻帶</string>
<string name="wifi_ap_choose_5G" msgid="8813128641914385634">5 GHz 頻帶</string>
<string name="wifi_ap_choose_6G">6 GHz 頻帶</string>
<string name="wifi_ap_choose_60G">60 GHz 頻帶</string>
<string name="wifi_ap_choose_G" msgid="8724267386885036210">%s GHz 頻帶</string>
<string name="wifi_advanced_mac_address_title" msgid="6571335466330978393">"MAC 地址"</string>
<string name="wifi_hidden_network" msgid="973162091800925000">"隱藏的網路"</string>
<string name="wifi_max_clients">允許的連接裝置數量</string>

View File

@@ -203,11 +203,7 @@
<string name="wifi_hotspot_timeout_default">Default timeout: %dms</string>
<string name="wifi_hotspot_ap_band_title">AP Band</string>
<string name="wifi_ap_choose_disabled">Disabled</string>
<string name="wifi_ap_choose_auto">Auto</string>
<string name="wifi_ap_choose_2G">2.4 GHz Band</string>
<string name="wifi_ap_choose_5G">5 GHz Band</string>
<string name="wifi_ap_choose_6G">6 GHz Band</string>
<string name="wifi_ap_choose_60G">60 GHz Band</string>
<string name="wifi_ap_choose_G">%s GHz Band</string>
<string name="wifi_hotspot_access_control_title">Access Control</string>
<string name="wifi_hotspot_ap_advanced_title">Advanced AP Options</string>
<string name="wifi_advanced_mac_address_title">MAC address</string>
@@ -217,7 +213,6 @@
<string name="wifi_blocked_list">Blocked list of clients</string>
<string name="wifi_allowed_list">Allowed list of clients</string>
<string name="wifi_mac_randomization">Use randomized MAC</string>
<string name="wifi_bridged_mode">Enable Bridged Access point (AP) concurrency</string>
<string name="wifi_bridged_mode_opportunistic_shutdown">Enable Bridged mode opportunistic shutdown</string>
<string name="wifi_ieee_80211ax">Enable Wi\u2011Fi 6</string>
<string name="wifi_user_config">User Supplied Configuration</string>