Refactor LOHService based on AOSP CarProjectionService

This commit is contained in:
Mygod
2021-06-11 02:20:55 -04:00
parent cc5cdec0c5
commit bb80359efb
5 changed files with 128 additions and 47 deletions

View File

@@ -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`
* (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 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 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`
* (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`

View File

@@ -6,16 +6,12 @@ import android.net.wifi.WifiManager
import android.os.Build
import androidx.annotation.RequiresApi
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.TetherTimeoutMonitor
import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat
import be.mygod.vpnhotspot.net.wifi.WifiApManager
import be.mygod.vpnhotspot.util.Services
import be.mygod.vpnhotspot.util.StickyEvent1
import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.coroutines.*
import timber.log.Timber
@@ -43,7 +39,8 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
null -> return // stopped
"" -> WifiApManager.cancelLocalOnlyHotspotRequest()
}
reservation?.close() ?: stopService()
reservation?.close()
stopService()
}
}
@@ -56,24 +53,6 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
override val coroutineContext = dispatcher + Job()
private var routingManager: RoutingManager? = 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 fun onBind(intent: Intent?) = binder
@@ -87,20 +66,37 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
override fun onStarted(reservation: WifiManager.LocalOnlyHotspotReservation?) {
if (reservation == null) onFailed(-2) else {
this@LocalOnlyHotspotService.reservation = reservation
if (!receiverRegistered) {
val configuration = binder.configuration!!
if (Build.VERSION.SDK_INT < 30 && configuration.isAutoShutdownEnabled) {
timeoutMonitor = TetherTimeoutMonitor(configuration.shutdownTimeoutMillis,
coroutineContext) { reservation.close() }
}
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
receiverRegistered = true
// based on: https://android.googlesource.com/platform/packages/services/Car/+/df5cd06/service/src/com/android/car/CarProjectionService.java#160
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() {
Timber.d("LOHCallback.onStopped")
reservation?.close()
reservation = null
}
@@ -152,15 +148,11 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope {
}
private fun unregisterReceiver(exit: Boolean = false) {
if (receiverRegistered) {
unregisterReceiver(receiver)
IpNeighbourMonitor.unregisterCallback(this)
if (Build.VERSION.SDK_INT >= 28) {
timeoutMonitor?.close()
timeoutMonitor = null
}
receiverRegistered = false
}
launch {
routingManager?.stop()
routingManager = null

View File

@@ -160,11 +160,11 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
}
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"))
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()
}
override fun onNumClientsChanged(numClients: Int) {

View File

@@ -134,7 +134,7 @@ object TetheringManager {
* Requires MANAGE_USB permission, unfortunately.
*
* 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)
const val TETHERING_USB = 1
@@ -142,14 +142,14 @@ object TetheringManager {
* Bluetooth tethering type.
*
* Requires BLUETOOTH permission.
* @see [startTethering].
* @see startTethering
*/
@RequiresApi(24)
const val TETHERING_BLUETOOTH = 2
/**
* Ncm local tethering type.
*
* @see [startTethering]
* @see startTethering
*/
@RequiresApi(30)
const val TETHERING_NCM = 4
@@ -157,7 +157,7 @@ object TetheringManager {
* Ethernet tethering type.
*
* Requires MANAGE_USB permission, also.
* @see [startTethering]
* @see startTethering
*/
@RequiresApi(30)
const val TETHERING_ETHERNET = 5

View File

@@ -59,6 +59,87 @@ object WifiApManager {
@get:RequiresApi(30)
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") }
@Suppress("DEPRECATION")
private val setWifiApConfiguration by lazy {
@@ -88,7 +169,7 @@ object WifiApManager {
/**
* 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_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
* @param failureReason reason when in failed state. One of
@@ -146,7 +227,7 @@ object WifiApManager {
@RequiresApi(30)
fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { }
}
@RequiresApi(28)
@RequiresApi(23)
val failureReasonLookup = ConstantLookup<WifiManager>("SAP_START_FAILURE_", "GENERAL", "NO_CHANNEL")
@get:RequiresApi(30)
val clientBlockLookup by lazy { ConstantLookup<WifiManager>("SAP_CLIENT_") }