Refactor LOHService based on AOSP CarProjectionService
This commit is contained in:
10
README.md
10
README.md
@@ -295,9 +295,17 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded
|
|||||||
* (on API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api`
|
* (on API 30) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Landroid/net/wifi/SoftApInfo;)V,sdk,system-api,test-api`
|
||||||
* (since API 31) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Ljava/util/List;)V,sdk,system-api,test-api`
|
* (since API 31) `Landroid/net/wifi/WifiManager$SoftApCallback;->onInfoChanged(Ljava/util/List;)V,sdk,system-api,test-api`
|
||||||
* (since API 28) `Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,sdk,system-api,test-api`
|
* (since API 28) `Landroid/net/wifi/WifiManager$SoftApCallback;->onStateChanged(II)V,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_FAILURE_REASON:Ljava/lang/String;,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_INTERFACE_NAME:Ljava/lang/String;,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String;,sdk,system-api,test-api`
|
||||||
* (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api`
|
* (since API 30) `Landroid/net/wifi/WifiManager;->SAP_CLIENT_BLOCK_REASON_CODE_*:I,sdk,system-api,test-api`
|
||||||
* (since API 28) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api`
|
* (since API 28) `Landroid/net/wifi/WifiManager;->SAP_START_FAILURE_*:I,sdk,system-api,test-api`
|
||||||
* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api`
|
* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I,sdk,system-api,test-api`
|
||||||
|
* (since API 26) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api`
|
||||||
* (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api`
|
* (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api`
|
||||||
* (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api`
|
* (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api`
|
||||||
* (since API 30) `Landroid/net/wifi/WifiManager;->isApMacRandomizationSupported()Z,sdk,system-api,test-api`
|
* (since API 30) `Landroid/net/wifi/WifiManager;->isApMacRandomizationSupported()Z,sdk,system-api,test-api`
|
||||||
|
|||||||
@@ -6,16 +6,12 @@ import android.net.wifi.WifiManager
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import be.mygod.vpnhotspot.net.IpNeighbour
|
import be.mygod.vpnhotspot.net.IpNeighbour
|
||||||
import be.mygod.vpnhotspot.net.TetherType
|
|
||||||
import be.mygod.vpnhotspot.net.TetheringManager
|
|
||||||
import be.mygod.vpnhotspot.net.TetheringManager.localOnlyTetheredIfaces
|
|
||||||
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
||||||
import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor
|
import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor
|
||||||
import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat
|
import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat
|
||||||
import be.mygod.vpnhotspot.net.wifi.WifiApManager
|
import be.mygod.vpnhotspot.net.wifi.WifiApManager
|
||||||
import be.mygod.vpnhotspot.util.Services
|
import be.mygod.vpnhotspot.util.Services
|
||||||
import be.mygod.vpnhotspot.util.StickyEvent1
|
import be.mygod.vpnhotspot.util.StickyEvent1
|
||||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -43,7 +39,8 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
|
|||||||
null -> return // stopped
|
null -> return // stopped
|
||||||
"" -> WifiApManager.cancelLocalOnlyHotspotRequest()
|
"" -> WifiApManager.cancelLocalOnlyHotspotRequest()
|
||||||
}
|
}
|
||||||
reservation?.close() ?: stopService()
|
reservation?.close()
|
||||||
|
stopService()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,24 +53,6 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
|
|||||||
override val coroutineContext = dispatcher + Job()
|
override val coroutineContext = dispatcher + Job()
|
||||||
private var routingManager: RoutingManager? = null
|
private var routingManager: RoutingManager? = null
|
||||||
private var timeoutMonitor: TetherTimeoutMonitor? = null
|
private var timeoutMonitor: TetherTimeoutMonitor? = null
|
||||||
private var receiverRegistered = false
|
|
||||||
private val receiver = broadcastReceiver { _, intent ->
|
|
||||||
val ifaces = (intent.localOnlyTetheredIfaces ?: return@broadcastReceiver).filter {
|
|
||||||
TetherType.ofInterface(it) != TetherType.WIFI_P2P
|
|
||||||
}
|
|
||||||
Timber.d("onTetherStateChangedLocked: $ifaces")
|
|
||||||
check(ifaces.size <= 1)
|
|
||||||
val iface = ifaces.singleOrNull()
|
|
||||||
binder.iface = iface
|
|
||||||
if (iface.isNullOrEmpty()) stopService() else launch {
|
|
||||||
val routingManager = routingManager
|
|
||||||
if (routingManager == null) {
|
|
||||||
this@LocalOnlyHotspotService.routingManager = RoutingManager.LocalOnly(this@LocalOnlyHotspotService,
|
|
||||||
iface).apply { start() }
|
|
||||||
IpNeighbourMonitor.registerCallback(this@LocalOnlyHotspotService)
|
|
||||||
} else check(iface == routingManager.downstream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override val activeIfaces get() = binder.iface.let { if (it.isNullOrEmpty()) emptyList() else listOf(it) }
|
override val activeIfaces get() = binder.iface.let { if (it.isNullOrEmpty()) emptyList() else listOf(it) }
|
||||||
|
|
||||||
override fun onBind(intent: Intent?) = binder
|
override fun onBind(intent: Intent?) = binder
|
||||||
@@ -87,20 +66,37 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
|
|||||||
override fun onStarted(reservation: WifiManager.LocalOnlyHotspotReservation?) {
|
override fun onStarted(reservation: WifiManager.LocalOnlyHotspotReservation?) {
|
||||||
if (reservation == null) onFailed(-2) else {
|
if (reservation == null) onFailed(-2) else {
|
||||||
this@LocalOnlyHotspotService.reservation = reservation
|
this@LocalOnlyHotspotService.reservation = reservation
|
||||||
if (!receiverRegistered) {
|
|
||||||
val configuration = binder.configuration!!
|
val configuration = binder.configuration!!
|
||||||
if (Build.VERSION.SDK_INT < 30 && configuration.isAutoShutdownEnabled) {
|
if (Build.VERSION.SDK_INT < 30 && configuration.isAutoShutdownEnabled) {
|
||||||
timeoutMonitor = TetherTimeoutMonitor(configuration.shutdownTimeoutMillis,
|
timeoutMonitor = TetherTimeoutMonitor(configuration.shutdownTimeoutMillis,
|
||||||
coroutineContext) { reservation.close() }
|
coroutineContext) { reservation.close() }
|
||||||
}
|
}
|
||||||
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
|
// based on: https://android.googlesource.com/platform/packages/services/Car/+/df5cd06/service/src/com/android/car/CarProjectionService.java#160
|
||||||
receiverRegistered = true
|
val sticky = registerReceiver(null, IntentFilter(WifiApManager.WIFI_AP_STATE_CHANGED_ACTION))!!
|
||||||
|
val apState = sticky.getIntExtra(WifiApManager.EXTRA_WIFI_AP_STATE,
|
||||||
|
WifiApManager.WIFI_AP_STATE_DISABLED)
|
||||||
|
val iface = sticky.getStringExtra(WifiApManager.EXTRA_WIFI_AP_INTERFACE_NAME)
|
||||||
|
if (apState != WifiApManager.WIFI_AP_STATE_ENABLED || iface.isNullOrEmpty()) {
|
||||||
|
if (apState == WifiApManager.WIFI_AP_STATE_FAILED) {
|
||||||
|
SmartSnackbar.make(getString(R.string.tethering_temp_hotspot_failure,
|
||||||
|
WifiApManager.failureReasonLookup(sticky.getIntExtra(
|
||||||
|
WifiApManager.EXTRA_WIFI_AP_FAILURE_REASON, 0)))).show()
|
||||||
|
}
|
||||||
|
return stopService()
|
||||||
|
}
|
||||||
|
binder.iface = iface
|
||||||
|
launch {
|
||||||
|
check(routingManager == null)
|
||||||
|
routingManager = RoutingManager.LocalOnly(
|
||||||
|
this@LocalOnlyHotspotService, iface).apply { start() }
|
||||||
|
IpNeighbourMonitor.registerCallback(this@LocalOnlyHotspotService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStopped() {
|
override fun onStopped() {
|
||||||
Timber.d("LOHCallback.onStopped")
|
Timber.d("LOHCallback.onStopped")
|
||||||
|
reservation?.close()
|
||||||
reservation = null
|
reservation = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,15 +148,11 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun unregisterReceiver(exit: Boolean = false) {
|
private fun unregisterReceiver(exit: Boolean = false) {
|
||||||
if (receiverRegistered) {
|
|
||||||
unregisterReceiver(receiver)
|
|
||||||
IpNeighbourMonitor.unregisterCallback(this)
|
IpNeighbourMonitor.unregisterCallback(this)
|
||||||
if (Build.VERSION.SDK_INT >= 28) {
|
if (Build.VERSION.SDK_INT >= 28) {
|
||||||
timeoutMonitor?.close()
|
timeoutMonitor?.close()
|
||||||
timeoutMonitor = null
|
timeoutMonitor = null
|
||||||
}
|
}
|
||||||
receiverRegistered = false
|
|
||||||
}
|
|
||||||
launch {
|
launch {
|
||||||
routingManager?.stop()
|
routingManager?.stop()
|
||||||
routingManager = null
|
routingManager = null
|
||||||
|
|||||||
@@ -160,11 +160,11 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStateChanged(state: Int, failureReason: Int) {
|
override fun onStateChanged(state: Int, failureReason: Int) {
|
||||||
if (state < 10 || state > 14) {
|
if (state < WifiApManager.WIFI_AP_STATE_DISABLING || state > WifiApManager.WIFI_AP_STATE_FAILED) {
|
||||||
Timber.w(Exception("Unknown state $state, $failureReason"))
|
Timber.w(Exception("Unknown state $state, $failureReason"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.failureReason = if (state == 14) failureReason else null // WIFI_AP_STATE_FAILED
|
this.failureReason = if (state == WifiApManager.WIFI_AP_STATE_FAILED) failureReason else null
|
||||||
data.notifyChange()
|
data.notifyChange()
|
||||||
}
|
}
|
||||||
override fun onNumClientsChanged(numClients: Int) {
|
override fun onNumClientsChanged(numClients: Int) {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ object TetheringManager {
|
|||||||
* Requires MANAGE_USB permission, unfortunately.
|
* Requires MANAGE_USB permission, unfortunately.
|
||||||
*
|
*
|
||||||
* Source: https://android.googlesource.com/platform/frameworks/base/+/7ca5d3a/services/usb/java/com/android/server/usb/UsbService.java#389
|
* Source: https://android.googlesource.com/platform/frameworks/base/+/7ca5d3a/services/usb/java/com/android/server/usb/UsbService.java#389
|
||||||
* @see [startTethering].
|
* @see startTethering
|
||||||
*/
|
*/
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
const val TETHERING_USB = 1
|
const val TETHERING_USB = 1
|
||||||
@@ -142,14 +142,14 @@ object TetheringManager {
|
|||||||
* Bluetooth tethering type.
|
* Bluetooth tethering type.
|
||||||
*
|
*
|
||||||
* Requires BLUETOOTH permission.
|
* Requires BLUETOOTH permission.
|
||||||
* @see [startTethering].
|
* @see startTethering
|
||||||
*/
|
*/
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
const val TETHERING_BLUETOOTH = 2
|
const val TETHERING_BLUETOOTH = 2
|
||||||
/**
|
/**
|
||||||
* Ncm local tethering type.
|
* Ncm local tethering type.
|
||||||
*
|
*
|
||||||
* @see [startTethering]
|
* @see startTethering
|
||||||
*/
|
*/
|
||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
const val TETHERING_NCM = 4
|
const val TETHERING_NCM = 4
|
||||||
@@ -157,7 +157,7 @@ object TetheringManager {
|
|||||||
* Ethernet tethering type.
|
* Ethernet tethering type.
|
||||||
*
|
*
|
||||||
* Requires MANAGE_USB permission, also.
|
* Requires MANAGE_USB permission, also.
|
||||||
* @see [startTethering]
|
* @see startTethering
|
||||||
*/
|
*/
|
||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
const val TETHERING_ETHERNET = 5
|
const val TETHERING_ETHERNET = 5
|
||||||
|
|||||||
@@ -59,6 +59,87 @@ object WifiApManager {
|
|||||||
@get:RequiresApi(30)
|
@get:RequiresApi(30)
|
||||||
val isApMacRandomizationSupported get() = apMacRandomizationSupported(Services.wifi) as Boolean
|
val isApMacRandomizationSupported get() = apMacRandomizationSupported(Services.wifi) as Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled,
|
||||||
|
* enabling, disabling, or failed.
|
||||||
|
*/
|
||||||
|
const val WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED"
|
||||||
|
/**
|
||||||
|
* The lookup key for an int that indicates whether Wi-Fi AP is enabled,
|
||||||
|
* disabled, enabling, disabling, or failed. Retrieve it with [Intent.getIntExtra].
|
||||||
|
*
|
||||||
|
* @see WIFI_AP_STATE_DISABLED
|
||||||
|
* @see WIFI_AP_STATE_DISABLING
|
||||||
|
* @see WIFI_AP_STATE_ENABLED
|
||||||
|
* @see WIFI_AP_STATE_ENABLING
|
||||||
|
* @see WIFI_AP_STATE_FAILED
|
||||||
|
*/
|
||||||
|
const val EXTRA_WIFI_AP_STATE = "wifi_state"
|
||||||
|
/**
|
||||||
|
* An extra containing the int error code for Soft AP start failure.
|
||||||
|
* Can be obtained from the [WIFI_AP_STATE_CHANGED_ACTION] using [Intent.getIntExtra].
|
||||||
|
* This extra will only be attached if [EXTRA_WIFI_AP_STATE] is
|
||||||
|
* attached and is equal to [WIFI_AP_STATE_FAILED].
|
||||||
|
*
|
||||||
|
* The error code will be one of:
|
||||||
|
* {@link #SAP_START_FAILURE_GENERAL},
|
||||||
|
* {@link #SAP_START_FAILURE_NO_CHANNEL},
|
||||||
|
* {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}
|
||||||
|
*
|
||||||
|
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-6.0.0_r1/wifi/java/android/net/wifi/WifiManager.java#210
|
||||||
|
*/
|
||||||
|
@get:RequiresApi(23)
|
||||||
|
val EXTRA_WIFI_AP_FAILURE_REASON get() =
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) "android.net.wifi.extra.WIFI_AP_FAILURE_REASON" else "wifi_ap_error_code"
|
||||||
|
/**
|
||||||
|
* The lookup key for a String extra that stores the interface name used for the Soft AP.
|
||||||
|
* This extra is included in the broadcast [WIFI_AP_STATE_CHANGED_ACTION].
|
||||||
|
* Retrieve its value with [Intent.getStringExtra].
|
||||||
|
*
|
||||||
|
* Source: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r1/wifi/java/android/net/wifi/WifiManager.java#413
|
||||||
|
*/
|
||||||
|
@get:RequiresApi(26)
|
||||||
|
val EXTRA_WIFI_AP_INTERFACE_NAME get() =
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME" else "wifi_ap_interface_name"
|
||||||
|
/**
|
||||||
|
* Wi-Fi AP is currently being disabled. The state will change to
|
||||||
|
* [WIFI_AP_STATE_DISABLED] if it finishes successfully.
|
||||||
|
*
|
||||||
|
* @see WIFI_AP_STATE_CHANGED_ACTION
|
||||||
|
* @see #getWifiApState()
|
||||||
|
*/
|
||||||
|
const val WIFI_AP_STATE_DISABLING = 10
|
||||||
|
/**
|
||||||
|
* Wi-Fi AP is disabled.
|
||||||
|
*
|
||||||
|
* @see WIFI_AP_STATE_CHANGED_ACTION
|
||||||
|
* @see #getWifiState()
|
||||||
|
*/
|
||||||
|
const val WIFI_AP_STATE_DISABLED = 11
|
||||||
|
/**
|
||||||
|
* Wi-Fi AP is currently being enabled. The state will change to
|
||||||
|
* {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
|
||||||
|
*
|
||||||
|
* @see WIFI_AP_STATE_CHANGED_ACTION
|
||||||
|
* @see #getWifiApState()
|
||||||
|
*/
|
||||||
|
const val WIFI_AP_STATE_ENABLING = 12
|
||||||
|
/**
|
||||||
|
* Wi-Fi AP is enabled.
|
||||||
|
*
|
||||||
|
* @see WIFI_AP_STATE_CHANGED_ACTION
|
||||||
|
* @see #getWifiApState()
|
||||||
|
*/
|
||||||
|
const val WIFI_AP_STATE_ENABLED = 13
|
||||||
|
/**
|
||||||
|
* Wi-Fi AP is in a failed state. This state will occur when an error occurs during
|
||||||
|
* enabling or disabling
|
||||||
|
*
|
||||||
|
* @see WIFI_AP_STATE_CHANGED_ACTION
|
||||||
|
* @see #getWifiApState()
|
||||||
|
*/
|
||||||
|
const val WIFI_AP_STATE_FAILED = 14
|
||||||
|
|
||||||
private val getWifiApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getWifiApConfiguration") }
|
private val getWifiApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getWifiApConfiguration") }
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private val setWifiApConfiguration by lazy {
|
private val setWifiApConfiguration by lazy {
|
||||||
@@ -88,7 +169,7 @@ object WifiApManager {
|
|||||||
/**
|
/**
|
||||||
* Called when soft AP state changes.
|
* Called when soft AP state changes.
|
||||||
*
|
*
|
||||||
* @param state the new AP state. One of {@link #WIFI_AP_STATE_DISABLED},
|
* @param state the new AP state. One of [WIFI_AP_STATE_DISABLED],
|
||||||
* {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
|
* {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
|
||||||
* {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
|
* {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
|
||||||
* @param failureReason reason when in failed state. One of
|
* @param failureReason reason when in failed state. One of
|
||||||
@@ -146,7 +227,7 @@ object WifiApManager {
|
|||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { }
|
fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { }
|
||||||
}
|
}
|
||||||
@RequiresApi(28)
|
@RequiresApi(23)
|
||||||
val failureReasonLookup = ConstantLookup<WifiManager>("SAP_START_FAILURE_", "GENERAL", "NO_CHANNEL")
|
val failureReasonLookup = ConstantLookup<WifiManager>("SAP_START_FAILURE_", "GENERAL", "NO_CHANNEL")
|
||||||
@get:RequiresApi(30)
|
@get:RequiresApi(30)
|
||||||
val clientBlockLookup by lazy { ConstantLookup<WifiManager>("SAP_CLIENT_") }
|
val clientBlockLookup by lazy { ConstantLookup<WifiManager>("SAP_CLIENT_") }
|
||||||
|
|||||||
Reference in New Issue
Block a user