diff --git a/README.md b/README.md index 43748af3..dfb14af6 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ Non-public API list: * (since API 24) [`Landroid/net/ConnectivityManager;->stopTethering(I)V,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#123410) * (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apBand:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#131529) * (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apChannel:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#131530) +* (since API 26) [`Landroid/net/wifi/WifiManager;->cancelLocalOnlyHotspotRequest()V,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132250) * [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132289) * [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,system-api,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/3d07e5c/appcompat/hiddenapi-flags.csv#132358) * (deprecated since API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z` diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt index f3aaa7f1..5a7ba26c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/LocalOnlyHotspotService.kt @@ -13,6 +13,7 @@ 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.WifiApManager import be.mygod.vpnhotspot.util.StickyEvent1 import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.widget.SmartSnackbar @@ -38,7 +39,17 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { val configuration get() = reservation?.wifiConfiguration - fun stop() = reservation?.close() + fun stop() = when (iface) { + null -> { } // stopped + "" -> { + WifiApManager.cancelLocalOnlyHotspotRequest() + reservation?.close() + updateNotification() + stopService() + iface = null + } + else -> reservation!!.close() + } } private val binder = Binder() @@ -59,11 +70,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { check(ifaces.size <= 1) val iface = ifaces.singleOrNull() binder.iface = iface - if (iface.isNullOrEmpty()) { - unregisterReceiver() - ServiceNotification.stopForeground(this) - stopSelf() - } else launch { + if (iface.isNullOrEmpty()) stopService() else launch { val routingManager = routingManager if (routingManager == null) { this@LocalOnlyHotspotService.routingManager = RoutingManager.LocalOnly(this@LocalOnlyHotspotService, @@ -72,7 +79,7 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { } else check(iface == routingManager.downstream) } } - override val activeIfaces get() = listOfNotNull(binder.iface) + override val activeIfaces get() = binder.iface.let { if (it.isNullOrEmpty()) emptyList() else listOf(it) } override fun onBind(intent: Intent?) = binder @@ -80,7 +87,9 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { if (binder.iface != null) return START_STICKY binder.iface = "" // show invisible foreground notification on television to avoid being killed - if (app.uiMode.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) updateNotification() + // or for bug with airplane mode on API 28-: https://issuetracker.google.com/issues/128350148 + if (app.uiMode.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION || + Build.VERSION.SDK_INT < 29) updateNotification() // throws IllegalStateException if the caller attempts to start the LocalOnlyHotspot while they // have an outstanding request. // https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/53e0284/service/java/com/android/server/wifi/WifiServiceImpl.java#1192 @@ -147,6 +156,12 @@ class LocalOnlyHotspotService : IpNeighbourMonitoringService(), CoroutineScope { super.onDestroy() } + private fun stopService() { + unregisterReceiver() + ServiceNotification.stopForeground(this) + stopSelf() + } + private fun unregisterReceiver(exit: Boolean = false) { if (receiverRegistered) { unregisterReceiver(receiver) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt index db30b982..dc37bb91 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotManager.kt @@ -79,7 +79,6 @@ class LocalOnlyHotspotManager(private val parent: TetheringFragment) : Manager() return lookup[binder?.iface ?: return ""]?.formatAddresses() ?: "" } override val active get() = binder?.iface != null - override val enabled get() = binder?.iface != "" override val selectable get() = active } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotTileService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotTileService.kt index 32c8fd79..2b25eb14 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotTileService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/LocalOnlyHotspotTileService.kt @@ -31,10 +31,10 @@ class LocalOnlyHotspotTileService : KillableTileService() { override fun onClick() { val binder = binder - if (binder == null) tapPending = true - else when (binder.iface) { - null -> ContextCompat.startForegroundService(this, Intent(this, LocalOnlyHotspotService::class.java)) - "" -> { } // STARTING, ignored + when { + binder == null -> tapPending = true + binder.iface == null -> ContextCompat.startForegroundService(this, + Intent(this, LocalOnlyHotspotService::class.java)) else -> binder.stop() } } @@ -44,19 +44,12 @@ class LocalOnlyHotspotTileService : KillableTileService() { service.ifaceChanged[this] = { qsTile?.run { icon = tile - when (it) { - null -> { - state = Tile.STATE_INACTIVE - label = getText(R.string.tethering_temp_hotspot) - } - "" -> { - state = Tile.STATE_UNAVAILABLE - label = getText(R.string.tethering_temp_hotspot) - } - else -> { - state = Tile.STATE_ACTIVE - label = service.configuration?.SSID ?: getText(R.string.tethering_temp_hotspot) - } + if (it.isNullOrEmpty()) { + state = Tile.STATE_INACTIVE + label = getText(R.string.tethering_temp_hotspot) + } else { + state = Tile.STATE_ACTIVE + label = service.configuration?.SSID ?: getText(R.string.tethering_temp_hotspot) } updateTile() } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt index 51f5b46b..043b7aba 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt @@ -17,6 +17,11 @@ object WifiApManager { } } + private val cancelLocalOnlyHotspotRequest by lazy { + WifiManager::class.java.getDeclaredMethod("cancelLocalOnlyHotspotRequest") + } + fun cancelLocalOnlyHotspotRequest() = cancelLocalOnlyHotspotRequest.invoke(app.wifi) + private val setWifiApEnabled by lazy { WifiManager::class.java.getDeclaredMethod("setWifiApEnabled", WifiConfiguration::class.java, Boolean::class.java)