diff options
| author | Albin <albin@mullvad.net> | 2022-05-24 10:21:58 +0200 |
|---|---|---|
| committer | Albin <albin@mullvad.net> | 2022-05-31 14:23:23 +0200 |
| commit | 817353a54b271e6d37671f56d02156faa54c685c (patch) | |
| tree | 15cea89234341c20eae7a1209db4f517c0b13b1a /android | |
| parent | ecb75765ff679454cf252af552a99a3e3380b145 (diff) | |
| download | mullvadvpn-817353a54b271e6d37671f56d02156faa54c685c.tar.xz mullvadvpn-817353a54b271e6d37671f56d02156faa54c685c.zip | |
Refactor Android service connection management
This refactor aims to be a step in decoupling the service connection
from the main activity and fragment inheritance. The goal is to avoid
or minimize the fragment inheritance and instead let each domain observe
service connection changes and adjust accordingly.
Diffstat (limited to 'android')
12 files changed, 172 insertions, 94 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index 17cfd3c922..ea1ae2b79b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers import net.mullvad.mullvadvpn.applist.ApplicationsIconManager import net.mullvad.mullvadvpn.applist.ApplicationsProvider import net.mullvad.mullvadvpn.ipc.EventDispatcher +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling import net.mullvad.mullvadvpn.viewmodel.SplitTunnelingViewModel import org.koin.android.ext.koin.androidContext @@ -30,6 +31,8 @@ val uiModule = module { SplitTunneling(messenger, dispatcher) } } + + single { ServiceConnectionManager(androidContext()) } } const val APPS_SCOPE = "APPS_SCOPE" const val SERVICE_CONNECTION_SCOPE = "SERVICE_CONNECTION_SCOPE" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt index bfb24c4f33..275981bd83 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt @@ -11,7 +11,7 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.Settings import net.mullvad.mullvadvpn.ui.customdns.CustomDnsAdapter import net.mullvad.mullvadvpn.ui.fragments.SplitTunnelingFragment -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer import net.mullvad.mullvadvpn.ui.widget.CellSwitch import net.mullvad.mullvadvpn.ui.widget.CustomRecyclerView import net.mullvad.mullvadvpn.ui.widget.MtuCell @@ -84,8 +84,8 @@ class AdvancedFragment : ServiceDependentFragment(OnNoService.GoBack) { return view } - override fun onNewServiceConnection(serviceConnection: ServiceConnection) { - super.onNewServiceConnection(serviceConnection) + override fun onNewServiceConnection(serviceConnectionContainer: ServiceConnectionContainer) { + super.onNewServiceConnection(serviceConnectionContainer) subscribeToCustomDnsChanges() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt index 9499d1c9f1..95d72fea9c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.onEach import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.DeviceState import net.mullvad.mullvadvpn.ui.serviceconnection.DeviceRepository -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer class LaunchFragment : ServiceAwareFragment() { @@ -32,8 +32,8 @@ class LaunchFragment : ServiceAwareFragment() { super.onStop() } - override fun onNewServiceConnection(serviceConnection: ServiceConnection) { - advanceToNextScreen(serviceConnection.deviceRepository) + override fun onNewServiceConnection(serviceConnectionContainer: ServiceConnectionContainer) { + advanceToNextScreen(serviceConnectionContainer.deviceRepository) } private fun advanceToNextScreen(deviceRepository: DeviceRepository) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt index 056bff9227..aa6c1a3ab0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt @@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.AccountHistory -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer import net.mullvad.mullvadvpn.ui.widget.AccountLogin import net.mullvad.mullvadvpn.ui.widget.HeaderBar import net.mullvad.mullvadvpn.viewmodel.LoginViewModel @@ -81,8 +81,8 @@ class LoginFragment : setupLifecycleSubscriptionsToViewModel() } - override fun onNewServiceConnection(serviceConnection: ServiceConnection) { - super.onNewServiceConnection(serviceConnection) + override fun onNewServiceConnection(serviceConnectionContainer: ServiceConnectionContainer) { + super.onNewServiceConnection(serviceConnectionContainer) if (this::loginViewModel.isInitialized) { loginViewModel.updateAccountCacheInstance(accountCache) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt index db092882d9..cb39336651 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt @@ -2,34 +2,26 @@ package net.mullvad.mullvadvpn.ui import android.app.Activity import android.app.UiModeManager -import android.content.ComponentName import android.content.Intent import android.content.pm.ActivityInfo import android.content.res.Configuration import android.net.VpnService import android.os.Bundle -import android.os.IBinder -import android.os.Messenger import android.util.Log import android.view.WindowManager import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager -import kotlin.properties.Delegates.observable import net.mullvad.mullvadvpn.BuildConfig import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.dataproxy.MullvadProblemReport import net.mullvad.mullvadvpn.di.uiModule -import net.mullvad.mullvadvpn.service.MullvadVpnService -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection -import net.mullvad.talpid.util.EventNotifier +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager +import org.koin.android.ext.android.getKoin import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules open class MainActivity : FragmentActivity() { - val problemReport = MullvadProblemReport() - val serviceNotifier = EventNotifier<ServiceConnection?>(null) private var visibleSecureScreens = HashSet<Fragment>() @@ -39,37 +31,13 @@ open class MainActivity : FragmentActivity() { uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION } - private var serviceConnection by observable<ServiceConnection?>( - null - ) { _, oldConnection, newConnection -> - oldConnection?.onDestroy() - - if (newConnection == null) { - serviceNotifier.notify(null) - } else { - newConnection.vpnPermission.onRequest = { -> - Unit - this.requestVpnPermission() - } - } - } - - private val serviceConnectionManager = object : android.content.ServiceConnection { - override fun onServiceConnected(className: ComponentName, binder: IBinder) { - android.util.Log.d("mullvad", "UI successfully connected to the service") - serviceConnection = ServiceConnection(Messenger(binder), ::handleNewServiceConnection) - } - - override fun onServiceDisconnected(className: ComponentName) { - android.util.Log.d("mullvad", "UI lost the connection to the service") - serviceConnection = null - } - } - var backButtonHandler: (() -> Boolean)? = null + private lateinit var serviceConnectionManager: ServiceConnectionManager + override fun onCreate(savedInstanceState: Bundle?) { loadKoinModules(uiModule) + serviceConnectionManager = getKoin().get() requestedOrientation = if (deviceIsTv) { ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE @@ -94,15 +62,11 @@ open class MainActivity : FragmentActivity() { override fun onStart() { Log.d("mullvad", "Starting main activity") super.onStart() - - val intent = Intent(this, MullvadVpnService::class.java) - - startService(intent) - bindService(intent, serviceConnectionManager, 0) + serviceConnectionManager.bind(vpnPermissionRequestHandler = ::requestVpnPermission) } override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { - serviceConnection?.vpnPermission?.grant(resultCode == Activity.RESULT_OK) + serviceConnectionManager.onVpnPermissionResult(resultCode == Activity.RESULT_OK) } override fun onBackPressed() { @@ -114,18 +78,16 @@ open class MainActivity : FragmentActivity() { } override fun onStop() { - Log.d("mullvad", "Stoping main activity") - unbindService(serviceConnectionManager) - + Log.d("mullvad", "Stopping main activity") super.onStop() - serviceConnection = null + // NOTE: `super.onStop()` must be called before unbinding due to the fragment state handling + // otherwise the fragments will believe there was an unexpected disconnect. + serviceConnectionManager.unbind() } override fun onDestroy() { - serviceNotifier.unsubscribeAll() - serviceConnection = null - + serviceConnectionManager.onDestroy() super.onDestroy() } @@ -174,10 +136,6 @@ open class MainActivity : FragmentActivity() { } } - private fun handleNewServiceConnection(connection: ServiceConnection) { - serviceNotifier.notify(connection) - } - @Suppress("DEPRECATION") private fun requestVpnPermission() { val intent = VpnService.prepare(this) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/RedeemVoucherDialogFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/RedeemVoucherDialogFragment.kt index eb64642a45..dce06a445c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/RedeemVoucherDialogFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/RedeemVoucherDialogFragment.kt @@ -18,15 +18,19 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.VoucherSubmissionError import net.mullvad.mullvadvpn.model.VoucherSubmissionResult import net.mullvad.mullvadvpn.ui.serviceconnection.AccountCache +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.VoucherRedeemer import net.mullvad.mullvadvpn.ui.widget.Button import net.mullvad.mullvadvpn.util.JobTracker import net.mullvad.mullvadvpn.util.SegmentedInputFormatter import org.joda.time.DateTime +import org.koin.android.ext.android.inject const val FULL_VOUCHER_CODE_LENGTH = "XXXX-XXXX-XXXX-XXXX".length class RedeemVoucherDialogFragment : DialogFragment() { + private val serviceConnectionManager: ServiceConnectionManager by inject() + private val jobTracker = JobTracker() private lateinit var parentActivity: MainActivity @@ -49,7 +53,7 @@ class RedeemVoucherDialogFragment : DialogFragment() { parentActivity = context as MainActivity - parentActivity.serviceNotifier.subscribe(this) { connection -> + serviceConnectionManager.serviceNotifier.subscribe(this) { connection -> accountCache = connection?.accountCache voucherRedeemer = connection?.voucherRedeemer } @@ -123,7 +127,7 @@ class RedeemVoucherDialogFragment : DialogFragment() { override fun onDetach() { jobTracker.cancelJob("updateExpiry") - parentActivity.serviceNotifier.unsubscribe(this) + serviceConnectionManager.serviceNotifier.unsubscribe(this) super.onDetach() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceAwareFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceAwareFragment.kt index 5788c60ad8..32ad70daaa 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceAwareFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceAwareFragment.kt @@ -2,10 +2,14 @@ package net.mullvad.mullvadvpn.ui import android.content.Context import net.mullvad.mullvadvpn.ui.fragments.BaseFragment -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.util.JobTracker +import org.koin.android.ext.android.inject abstract class ServiceAwareFragment : BaseFragment() { + private val serviceConnectionManager: ServiceConnectionManager by inject() + val jobTracker = JobTracker() open val isSecureScreen = false @@ -13,7 +17,7 @@ abstract class ServiceAwareFragment : BaseFragment() { lateinit var parentActivity: MainActivity private set - var serviceConnection: ServiceConnection? = null + var serviceConnectionContainer: ServiceConnectionContainer? = null private set override fun onAttach(context: Context) { @@ -25,7 +29,7 @@ abstract class ServiceAwareFragment : BaseFragment() { parentActivity.enterSecureScreen(this) } - parentActivity.serviceNotifier.subscribe(this) { connection -> + serviceConnectionManager.serviceNotifier.subscribe(this) { connection -> configureServiceConnection(connection) } } @@ -37,7 +41,7 @@ abstract class ServiceAwareFragment : BaseFragment() { } override fun onDetach() { - parentActivity.serviceNotifier.unsubscribe(this) + serviceConnectionManager.serviceNotifier.unsubscribe(this) if (isSecureScreen) { parentActivity.leaveSecureScreen(this) @@ -46,16 +50,18 @@ abstract class ServiceAwareFragment : BaseFragment() { super.onDetach() } - abstract fun onNewServiceConnection(serviceConnection: ServiceConnection) + abstract fun onNewServiceConnection(serviceConnectionContainer: ServiceConnectionContainer) open fun onNoServiceConnection() { } - private fun configureServiceConnection(connection: ServiceConnection?) { - serviceConnection = connection + private fun configureServiceConnection( + serviceConnectionContainer: ServiceConnectionContainer? + ) { + this.serviceConnectionContainer = serviceConnectionContainer - if (connection != null) { - onNewServiceConnection(connection) + if (serviceConnectionContainer != null) { + onNewServiceConnection(serviceConnectionContainer) } else { onNoServiceConnection() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt index f40f497857..5202960e81 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt @@ -13,7 +13,7 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.CustomDns import net.mullvad.mullvadvpn.ui.serviceconnection.DeviceRepository import net.mullvad.mullvadvpn.ui.serviceconnection.LocationInfoCache import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer import net.mullvad.mullvadvpn.ui.serviceconnection.SettingsListener import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling @@ -63,19 +63,19 @@ abstract class ServiceDependentFragment(private val onNoService: OnNoService) : lateinit var splitTunneling: SplitTunneling private set - override fun onNewServiceConnection(serviceConnection: ServiceConnection) { + override fun onNewServiceConnection(serviceConnectionContainer: ServiceConnectionContainer) { // This method is always either called first or after an `onNoServiceConnection`, so the // initialization of the fields doesn't have to be synchronized - accountCache = serviceConnection.accountCache - appVersionInfoCache = serviceConnection.appVersionInfoCache - authTokenCache = serviceConnection.authTokenCache - connectionProxy = serviceConnection.connectionProxy - deviceRepository = serviceConnection.deviceRepository - customDns = serviceConnection.customDns - locationInfoCache = serviceConnection.locationInfoCache - relayListListener = serviceConnection.relayListListener - settingsListener = serviceConnection.settingsListener - splitTunneling = serviceConnection.splitTunneling + accountCache = serviceConnectionContainer.accountCache + appVersionInfoCache = serviceConnectionContainer.appVersionInfoCache + authTokenCache = serviceConnectionContainer.authTokenCache + connectionProxy = serviceConnectionContainer.connectionProxy + deviceRepository = serviceConnectionContainer.deviceRepository + customDns = serviceConnectionContainer.customDns + locationInfoCache = serviceConnectionContainer.locationInfoCache + relayListListener = serviceConnectionContainer.relayListListener + settingsListener = serviceConnectionContainer.settingsListener + splitTunneling = serviceConnectionContainer.splitTunneling synchronized(this) { when (state) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt index 08afd5c59e..cc0de9d2d7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt @@ -15,7 +15,7 @@ import net.mullvad.mullvadvpn.model.DeviceState import net.mullvad.mullvadvpn.ui.serviceconnection.AccountCache import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoCache import net.mullvad.mullvadvpn.ui.serviceconnection.DeviceRepository -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer import net.mullvad.mullvadvpn.ui.widget.AccountCell import net.mullvad.mullvadvpn.ui.widget.AppVersionCell import net.mullvad.mullvadvpn.ui.widget.NavigateCell @@ -33,10 +33,10 @@ class SettingsFragment : ServiceAwareFragment(), StatusBarPainter, NavigationBar private var deviceRepository: DeviceRepository? = null private var versionInfoCache: AppVersionInfoCache? = null - override fun onNewServiceConnection(serviceConnection: ServiceConnection) { - accountCache = serviceConnection.accountCache - deviceRepository = serviceConnection.deviceRepository - versionInfoCache = serviceConnection.appVersionInfoCache + override fun onNewServiceConnection(serviceConnectionContainer: ServiceConnectionContainer) { + accountCache = serviceConnectionContainer.accountCache + deviceRepository = serviceConnectionContainer.deviceRepository + versionInfoCache = serviceConnectionContainer.appVersionInfoCache if (active) { configureListeners() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt index d957fc8614..d0d13ac330 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt @@ -20,9 +20,10 @@ import org.koin.core.scope.get // The properties of this class can be used to send events to the service, to listen for events from // the service and to get values received from events. @OptIn(KoinApiExtension::class) -class ServiceConnection( +class ServiceConnectionContainer( connection: Messenger, - onServiceReady: ((ServiceConnection) -> Unit)? = null + onServiceReady: (ServiceConnectionContainer) -> Unit, + onVpnPermissionRequest: () -> Unit ) : KoinScopeComponent { private val dispatcher = DispatchingHandler(Looper.getMainLooper()) { message -> Event.fromMessage(message) @@ -50,8 +51,10 @@ class ServiceConnection( var relayListListener = RelayListListener(connection, dispatcher, settingsListener) init { + vpnPermission.onRequest = onVpnPermissionRequest + dispatcher.registerHandler(Event.ListenerReady::class) { _ -> - onServiceReady?.invoke(this@ServiceConnection) + onServiceReady.invoke(this@ServiceConnectionContainer) } registerListener(connection) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt new file mode 100644 index 0000000000..55270863e8 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt @@ -0,0 +1,94 @@ +package net.mullvad.mullvadvpn.ui.serviceconnection + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.os.IBinder +import android.os.Messenger +import android.util.Log +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import net.mullvad.mullvadvpn.service.MullvadVpnService +import net.mullvad.talpid.util.EventNotifier + +class ServiceConnectionManager( + private val context: Context +) { + private val _connectionState = + MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected) + + val connectionState = _connectionState.asStateFlow() + + // TODO: Remove after refactoring fragments to support flow. + @Deprecated(message = "Use connectionState") + val serviceNotifier = EventNotifier<ServiceConnectionContainer?>(null) + + private var vpnPermissionRequestHandler: (() -> Unit)? = null + + private val serviceConnection = object : android.content.ServiceConnection { + override fun onServiceConnected(className: ComponentName, binder: IBinder) { + Log.d("mullvad", "UI successfully connected to the service") + + notify( + ServiceConnectionState.ConnectedNotReady( + ServiceConnectionContainer( + Messenger(binder), + ::handleNewServiceConnection, + ::handleVpnPermissionRequest + ) + ) + ) + } + + override fun onServiceDisconnected(className: ComponentName) { + Log.d("mullvad", "UI lost the connection to the service") + notify(ServiceConnectionState.Disconnected) + } + } + + fun bind(vpnPermissionRequestHandler: () -> Unit) { + this.vpnPermissionRequestHandler = vpnPermissionRequestHandler + val intent = Intent(context, MullvadVpnService::class.java) + context.startService(intent) + context.bindService(intent, serviceConnection, 0) + } + + fun unbind() { + context.unbindService(serviceConnection) + notify(ServiceConnectionState.Disconnected) + vpnPermissionRequestHandler = null + } + + fun onDestroy() { + serviceNotifier.unsubscribeAll() + notify(ServiceConnectionState.Disconnected) + vpnPermissionRequestHandler = null + } + + fun onVpnPermissionResult(isGranted: Boolean) { + _connectionState.value.let { state -> + if (state is ServiceConnectionState.ConnectedReady) { + state.container.vpnPermission.grant(isGranted) + } + } + } + + private fun notify(state: ServiceConnectionState) { + _connectionState.value = state + + // TODO: Remove once `serviceNotifier` is no longer used. + if (state is ServiceConnectionState.ConnectedReady) { + serviceNotifier.notify(state.container) + } else if (state is ServiceConnectionState.Disconnected) { + serviceNotifier.notify(null) + } + } + + private fun handleVpnPermissionRequest() { + vpnPermissionRequestHandler?.invoke() + } + + private fun handleNewServiceConnection(serviceConnectionContainer: ServiceConnectionContainer) { + notify(ServiceConnectionState.ConnectedReady(serviceConnectionContainer)) + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionState.kt new file mode 100644 index 0000000000..8e48f3a85c --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionState.kt @@ -0,0 +1,10 @@ +package net.mullvad.mullvadvpn.ui.serviceconnection + +sealed class ServiceConnectionState { + data class ConnectedReady(val container: ServiceConnectionContainer) : ServiceConnectionState() + + data class ConnectedNotReady(val container: ServiceConnectionContainer) : + ServiceConnectionState() + + object Disconnected : ServiceConnectionState() +} |
