diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index 0e98c14a..77e09b28 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -2,7 +2,6 @@ package be.mygod.vpnhotspot import android.app.Service import android.content.Intent -import android.content.IntentFilter import android.content.SharedPreferences import android.net.NetworkInfo import android.net.wifi.WpsInfo @@ -58,6 +57,11 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere val service get() = this@RepeaterService val active get() = status == Status.ACTIVE val statusChanged = StickyEvent0() + var group: WifiP2pGroup? = null + set(value) { + field = value + groupChanged(value) + } val groupChanged = StickyEvent1 { group } fun startWps(pin: String? = null) { @@ -92,38 +96,10 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere formatReason(R.string.repeater_reset_credentials_failure, reason)).show() }) } - - fun requestGroupUpdate() { - group = null - val channel = channel ?: return - val device = thisDevice ?: return - try { - p2pManager.requestPersistentGroupInfo(channel) { - val ownedGroups = it.filter { it.isGroupOwner && it.owner.deviceAddress == device.deviceAddress } - val main = ownedGroups.minBy { it.netId } - group = main - if (main != null) ownedGroups.filter { it.netId != main.netId }.forEach { - p2pManager.deletePersistentGroup(channel, it.netId, object : WifiP2pManager.ActionListener { - override fun onSuccess() = Timber.i("Removed redundant owned group: $it") - override fun onFailure(reason: Int) = SmartSnackbar.make( - formatReason(R.string.repeater_clean_pog_failure, reason)).show() - }) - } - } - } catch (e: ReflectiveOperationException) { - Timber.w(e) - SmartSnackbar.make(e).show() - } - } } private val p2pManager get() = RepeaterService.p2pManager!! private var channel: WifiP2pManager.Channel? = null - var group: WifiP2pGroup? = null - private set(value) { - field = value - binder.groupChanged(value) - } private val binder = Binder() private var receiverRegistered = false private val receiver = broadcastReceiver { _, intent -> @@ -140,8 +116,13 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere private var routingManager: LocalOnlyInterfaceManager? = null private var thisDevice: WifiP2pDevice? = null - private val thisDeviceListener = broadcastReceiver { _, intent -> - thisDevice = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE) + private val deviceListener = broadcastReceiver { _, intent -> + when (intent.action) { + WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> { + thisDevice = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE) + } + WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION -> onPersistentGroupsChanged() + } } var status = Status.IDLE @@ -164,8 +145,9 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere override fun onCreate() { super.onCreate() - registerReceiver(thisDeviceListener, IntentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)) onChannelDisconnected() + registerReceiver(deviceListener, intentFilter(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION, + WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION)) app.pref.registerOnSharedPreferenceChangeListener(this) } @@ -194,7 +176,6 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere if (status != Status.DESTROYED) try { channel = p2pManager.initialize(this, Looper.getMainLooper(), this) setOperatingChannel() - binder.requestGroupUpdate() } catch (e: RuntimeException) { Timber.w(e) app.handler.postDelayed(1000, this, this::onChannelDisconnected) @@ -205,6 +186,29 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere if (key == App.KEY_OPERATING_CHANNEL) setOperatingChannel() } + private fun onPersistentGroupsChanged() { + val channel = channel ?: return + val device = thisDevice ?: return + try { + p2pManager.requestPersistentGroupInfo(channel) { + val ownedGroups = it.filter { it.isGroupOwner && it.owner.deviceAddress == device.deviceAddress } + val main = ownedGroups.minBy { it.netId } + // do not replace current group if it's better + if (binder.group?.passphrase == null) binder.group = main + if (main != null) ownedGroups.filter { it.netId != main.netId }.forEach { + p2pManager.deletePersistentGroup(channel, it.netId, object : WifiP2pManager.ActionListener { + override fun onSuccess() = Timber.i("Removed redundant owned group: $it") + override fun onFailure(reason: Int) = SmartSnackbar.make( + formatReason(R.string.repeater_clean_pog_failure, reason)).show() + }) + } + } + } catch (e: ReflectiveOperationException) { + Timber.w(e) + SmartSnackbar.make(e).show() + } + } + /** * startService Step 1 */ @@ -248,7 +252,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere if (routingManager != null) clean() // P2P shutdown, else other groups changing before start, ignore } routingManager != null -> { - this.group = group + binder.group = group showNotification(group) } else -> doStart(group) @@ -258,7 +262,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere * startService Step 3 */ private fun doStart(group: WifiP2pGroup) { - this.group = group + binder.group = group check(routingManager == null) routingManager = LocalOnlyInterfaceManager(group.`interface`!!) status = Status.ACTIVE @@ -304,9 +308,9 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere if (status != Status.IDLE) binder.shutdown() clean() // force clean to prevent leakage app.pref.unregisterOnSharedPreferenceChangeListener(this) + unregisterReceiver(deviceListener) status = Status.DESTROYED if (Build.VERSION.SDK_INT >= 27) channel?.close() - unregisterReceiver(thisDeviceListener) super.onDestroy() } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterTileService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterTileService.kt index e1b50b82..4979d632 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterTileService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterTileService.kt @@ -56,7 +56,7 @@ class RepeaterTileService : TileService(), ServiceConnection { binder.groupChanged -= this } - private fun updateTile(group: WifiP2pGroup? = binder?.service?.group) { + private fun updateTile(group: WifiP2pGroup? = binder?.group) { val qsTile = qsTile ?: return when (binder?.service?.status) { RepeaterService.Status.IDLE -> { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt index 44d155b4..5565cec8 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientViewModel.kt @@ -30,7 +30,7 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb private fun populateClients() { val clients = HashMap, Client>() - val group = repeater?.service?.group + val group = repeater?.group val p2pInterface = group?.`interface` if (p2pInterface != null) { for (client in p2p) clients[Pair(p2pInterface, client.deviceAddress)] = WifiP2pClient(p2pInterface, client) @@ -50,7 +50,7 @@ class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callb private fun refreshP2p() { val repeater = repeater - p2p = (if (repeater?.active != true) null else repeater.service.group?.clientList) ?: emptyList() + p2p = (if (repeater?.active != true) null else repeater.group?.clientList) ?: emptyList() populateClients() } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index aad8cfa0..ab1451d2 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -42,7 +42,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic else -> false } - val ssid @Bindable get() = binder?.service?.group?.networkName ?: "" + val ssid @Bindable get() = binder?.group?.networkName ?: "" val addresses: CharSequence @Bindable get() { return try { NetworkInterface.getByName(p2pInterface ?: return "")?.formatAddresses() ?: "" @@ -90,7 +90,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic fun editConfigurations() { val binder = binder - val group = binder?.service?.group + val group = binder?.group val ssid = group?.networkName if (ssid != null) { val wifi = WifiConfiguration() @@ -171,7 +171,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic DialogInterface.BUTTON_POSITIVE -> try { data.getParcelableExtra(WifiP2pDialogFragment.KEY_CONFIGURER) .update(data.getParcelableExtra(WifiP2pDialogFragment.KEY_CONFIGURATION)) - app.handler.postDelayed(binder!!::requestGroupUpdate, 1000) + binder!!.group = null } catch (e: Exception) { Timber.w(e) SmartSnackbar.make(e).show() diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt index bb6aa1d3..642d28fa 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiP2pManagerHelper.kt @@ -10,6 +10,7 @@ import java.lang.reflect.Proxy object WifiP2pManagerHelper { const val UNSUPPORTED = -2 + const val WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION = "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" /** * Available since Android 4.4.