Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mikedawson/f6f5c69d23ce7228dcaf2e03f933777b to your computer and use it in GitHub Desktop.
Save mikedawson/f6f5c69d23ce7228dcaf2e03f933777b to your computer and use it in GitHub Desktop.
Android 13+ LocalOnlyHotspot custom config
/**
* Reflection workaround to access hidden SoftApConfiguration.Builder so it can be used to set
* LocalOnlyHotspot on Android 13+
*
* LocalOnlyHotspotConfig is generated here by Android:
* https://cs.android.com/android/platform/superproject/+/refs/heads/master:packages/modules/Wifi/service/java/com/android/server/wifi/WifiApConfigStore.java;drc=7bb4243a97d53af6cbd4de21bcc61556a758898b;l=423
*/
@RequiresApi(30)
class UnhiddenSoftApConfigurationBuilder {
@SuppressLint("PrivateApi")
private val builderClass = Class.forName("android.net.wifi.SoftApConfiguration\$Builder")
private val builderInstance = builderClass.newInstance()
fun setBand(band: Int) : UnhiddenSoftApConfigurationBuilder {
builderClass.getMethod("setBand", Int::class.javaPrimitiveType).invoke(
builderInstance, band
)
return this
}
fun setAutoshutdownEnabled(enabled: Boolean) : UnhiddenSoftApConfigurationBuilder {
builderClass.getMethod("setAutoShutdownEnabled", Boolean::class.javaPrimitiveType).invoke(
builderInstance, enabled
)
return this
}
fun setPassphrase(passphrase: String, securityType: Int) : UnhiddenSoftApConfigurationBuilder {
builderClass.getMethod(
"setPassphrase", String::class.java, Int::class.javaPrimitiveType
).invoke(
builderInstance, passphrase, securityType
)
return this
}
fun setBssid(macAddress: MacAddress) : UnhiddenSoftApConfigurationBuilder {
builderClass.getMethod("setBssid", MacAddress::class.java).invoke(
builderInstance, macAddress
)
return this
}
fun setMacRandomizationSetting(randomizationSetting: Int): UnhiddenSoftApConfigurationBuilder {
builderClass.getMethod("setMacRandomizationSetting", Int::class.java).invoke(
builderInstance, randomizationSetting
)
return this
}
fun setSsid(ssid: String) : UnhiddenSoftApConfigurationBuilder {
builderClass.getMethod("setSsid", String::class.java).invoke(
builderInstance, ssid
)
return this
}
fun build(): SoftApConfiguration {
return builderClass
.getMethod("build")
.invoke(builderInstance) as SoftApConfiguration
}
companion object {
//Band constants as per
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r54:packages/modules/Wifi/framework/java/android/net/wifi/SoftApConfiguration.java;l=78
const val BAND_2GHZ = 1.shl(0)
const val BAND_5GHZ = 1.shl(1)
const val BAND_6GHZ = 1.shl(2)
const val BAND_60GHZ = 1.shl(3)
const val BAND_ANY = BAND_2GHZ.or(BAND_5GHZ).or(BAND_6GHZ).or(BAND_60GHZ)
/*
* Randomization settings as per
* https://cs.android.com/android/platform/superproject/+/android-13.0.0_r54:packages/modules/Wifi/framework/java/android/net/wifi/SoftApConfiguration.java;l=344
*/
const val RANDOMIZATION_NONE = 0
const val RANDOMIZATION_PERSISTENT = 1
const val RANDOMIZATION_NON_PERSISTENT = 2
/*
* Security types as per
* https://cs.android.com/android/platform/superproject/+/android-13.0.0_r54:packages/modules/Wifi/framework/java/android/net/wifi/SoftApConfiguration.java;l=406
*/
/**
* THe definition of security type OPEN.
*/
const val SECURITY_TYPE_OPEN = 0
/**
* The definition of security type WPA2-PSK.
*/
const val SECURITY_TYPE_WPA2_PSK = 1
/**
* The definition of security type WPA3-SAE Transition mode.
*/
const val SECURITY_TYPE_WPA3_SAE_TRANSITION = 2
/**
* The definition of security type WPA3-SAE.
*/
const val SECURITY_TYPE_WPA3_SAE = 3
/**
* The definition of security type WPA3-OWE Transition.
*/
const val SECURITY_TYPE_WPA3_OWE_TRANSITION = 4
/**
* The definition of security type WPA3-OWE.
*/
const val SECURITY_TYPE_WPA3_OWE = 5
}
}
/**
* Start a LocalOnlyHotspot using a configuration to set the band, ssid, bssid, etc. This
* a hidden API and works only with Android 13+. The API requires one of three permissions:
* NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, or NEARBY_WIFI_DEVICES. The first two are reserved for
* system apps. NEARBY_WIFI_DEVICES permission is a runtime permission that is available from
* Android 12 and up.
*
* This calls the hidden function startLocalOnlyHotspot(SoftApConfig, Executor, callback):
* https://cs.android.com/android/platform/superproject/+/android-13.0.0_r1:packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java;l=4764
*
* Unfortunately Android 12 does not accept NEARBY_WIFI_DEVICES permission - if the platform or target
* is less than SDK 33, it will fail as per the implementation of WifiServiceImpl on Android 12:
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:packages/modules/Wifi/service/java/com/android/server/wifi/WifiServiceImpl.java;l=2045;bpv=1;bpt=0
*
* From Android 13 the check is changed to accept NEARBY_WIFI_DEVICES permission (provided that the
* device and target SDK is Android 13 or higher) when a configuration is provided:
* https://cs.android.com/android/platform/superproject/+/android-13.0.0_r1:packages/modules/Wifi/service/java/com/android/server/wifi/WifiServiceImpl.java;l=2472
*
* ===It is NOT possible to avoid a random MAC for LocalOnlyHotspot on Android 11/12.===
*
* It might be possible to use the accessibility service to automatically dismiss accept dialogs
* to mitigate this for large deployments etc.
*
* see: https://developer.android.com/guide/topics/ui/accessibility/service
* e.g. https://tooploox.com/unusual-ways-using-android-accessibility-services
*
* Avenues explored:
*
* Option 1: use WifiManager.setWifiApConfiguration to set the default tethering ap configuration,
* and set mac randomization to NONE. This is blocked by a requirement for OVERRIDE_WIFI_CONFIG
* permission (which is not available to third party app). See WifiServiceImpl#setSoftApConfiguration
* https://cs.android.com/android/platform/superproject/+/android-13.0.0_r54:packages/modules/Wifi/service/java/com/android/server/wifi/WifiServiceImpl.java;l=2727
*
* Option 2: try to disable Wifi Mac randomization so that the generateLocalOnlyHotspotConfig will
* set randomization to NONE. Won't work: AP config util uses a system resource
* R.bool.config_wifi_ap_mac_randomization_supported as per:
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:packages/modules/Wifi/service/java/com/android/server/wifi/util/ApConfigUtil.java;l=699
* The only way to override that would be a "Runtime Resourc Overlay" RRO, which can only be installed
* with system level permission.
*
* Option 3: Try using global settings as per
* https://stackoverflow.com/questions/61748195/android-10-how-to-disable-randomized-mac-address-in-source-code
* Won't work on Android 11/12 - Mac Randomization is now a per-network setting, not a global setting.
* Might work on Android 9 as per
* https://github.com/elastic/AndroidSDKMirror-28/blob/master/com/android/server/wifi/WifiStateMachine.java
*
* Option 4: Use adb command / network settings app to change the default tethering, but the
* settings app itself does not change this:
* https://cs.android.com/android/platform/superproject/+/master:packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSettings.java;l=231
* adb shell cmd wifi also does not support changing this.
*
*/
@RequiresApi(33)
fun WifiManager.startLocalOnlyHotspotWithConfig(
config: SoftApConfiguration,
executor: Executor?,
callback: LocalOnlyHotspotCallback
) {
WifiManager::class.java.getMethod(
"startLocalOnlyHotspot", SoftApConfiguration::class.java, Executor::class.java,
LocalOnlyHotspotCallback::class.java,
).invoke(this, config, executor, callback)
}
/**
* It is NOT possible to workaround the random MAC problem on Android 11/12.
*
* Option 1: use WifiManager.setWifiApConfiguration to set the default tethering ap configuration,
* and set mac randomization to NONE. This is blocked by a requirement for OVERRIDE_WIFI_CONFIG
* permission (which is not available to third party app). See WifiServiceImpl#setSoftApConfiguration
https://cs.android.com/android/platform/superproject/+/android-13.0.0_r54:packages/modules/Wifi/service/java/com/android/server/wifi/WifiServiceImpl.java;l=2727
*
* Option 2: try to disable Wifi Mac randomization so that the generateLocalOnlyHotspotConfig will
* set randomization to NONE. Won't work: AP config util uses a system resource
* R.bool.config_wifi_ap_mac_randomization_supported as per:
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:packages/modules/Wifi/service/java/com/android/server/wifi/util/ApConfigUtil.java;l=699
* The only way to override that would be a "Runtime Resourc Overlay" RRO, which can only be installed
* with system level permission.
*
* Option 3: Try using global settings as per
* https://stackoverflow.com/questions/61748195/android-10-how-to-disable-randomized-mac-address-in-source-code
* Won't work on Android 11/12 - Mac Randomization is now a per-network setting, not a global setting.
* Might work on Android 9 as per
* https://github.com/elastic/AndroidSDKMirror-28/blob/master/com/android/server/wifi/WifiStateMachine.java
*
* Option 4: Use adb command / network settings app to change the default tethering, but the
* settings app itself does not change this:
* https://cs.android.com/android/platform/superproject/+/master:packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSettings.java;l=231
*
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment