diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-20 12:36:11 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-20 12:36:11 -0300 |
| commit | f9c55e39194a22f78916ff0d0734eb9d79c4f388 (patch) | |
| tree | e718af03694f87666ccf0a7e8cfbc66b081a8117 | |
| parent | a358f925e83ebbdd838aa6a2162c48fe15a9d025 (diff) | |
| parent | b02e868411d3c112a05d3ea09691c308e6bf5131 (diff) | |
| download | mullvadvpn-f9c55e39194a22f78916ff0d0734eb9d79c4f388.tar.xz mullvadvpn-f9c55e39194a22f78916ff0d0734eb9d79c4f388.zip | |
Merge branch 'android-split-tunnelling'
9 files changed, 98 insertions, 6 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt index fe9c223194..820ef12fb2 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt @@ -6,9 +6,13 @@ import android.view.LayoutInflater import android.view.ViewGroup import kotlin.properties.Delegates.observable import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.service.SplitTunnelling import net.mullvad.mullvadvpn.util.JobTracker -class AppListAdapter(context: Context) : Adapter<AppListItemHolder>() { +class AppListAdapter( + context: Context, + private val splitTunnelling: SplitTunnelling +) : Adapter<AppListItemHolder>() { private val appList = ArrayList<AppInfo>() private val jobTracker = JobTracker() private val packageManager = context.packageManager @@ -41,7 +45,7 @@ class AppListAdapter(context: Context) : Adapter<AppListItemHolder>() { val inflater = LayoutInflater.from(parentView.context) val view = inflater.inflate(R.layout.app_list_item, parentView, false) - return AppListItemHolder(packageManager, jobTracker, view) + return AppListItemHolder(splitTunnelling, packageManager, jobTracker, view) } override fun onBindViewHolder(holder: AppListItemHolder, position: Int) { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt index 04bdd55686..b2d726f25e 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt @@ -8,10 +8,12 @@ import android.widget.ImageView import android.widget.TextView import kotlin.properties.Delegates.observable import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.service.SplitTunnelling import net.mullvad.mullvadvpn.ui.CellSwitch import net.mullvad.mullvadvpn.util.JobTracker class AppListItemHolder( + private val splitTunnelling: SplitTunnelling, private val packageManager: PackageManager, private val jobTracker: JobTracker, view: View @@ -33,6 +35,12 @@ class AppListItemHolder( hideIcon() loadIcon(info) } + + if (splitTunnelling.isAppExcluded(info.info.packageName)) { + excluded.forcefullySetState(CellSwitch.State.ON) + } else { + excluded.forcefullySetState(CellSwitch.State.OFF) + } } else { name.text = "" hideIcon() @@ -43,6 +51,15 @@ class AppListItemHolder( view.setOnClickListener { excluded.toggle() } + + excluded.listener = { state -> + appInfo?.info?.packageName?.let { app -> + when (state) { + CellSwitch.State.ON -> splitTunnelling.excludeApp(app) + CellSwitch.State.OFF -> splitTunnelling.includeApp(app) + } + } + } } private fun hideIcon() { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt index edc0cfd21b..317ca72eed 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt @@ -242,7 +242,21 @@ class MullvadVpnService : TalpidVpnService() { pendingAction = null } - instance = ServiceInstance(daemon, connectionProxy, connectivityListener, settingsListener) + val splitTunnelling = SplitTunnelling().apply { + onChange = { excludedApps -> + disallowedApps = excludedApps + markTunAsStale() + connectionProxy.reconnect() + } + } + + instance = ServiceInstance( + daemon, + connectionProxy, + connectivityListener, + settingsListener, + splitTunnelling + ) } private fun stop() { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt index 273955bcef..ecca6b12f0 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt @@ -6,7 +6,8 @@ class ServiceInstance( val daemon: MullvadDaemon, val connectionProxy: ConnectionProxy, val connectivityListener: ConnectivityListener, - val settingsListener: SettingsListener + val settingsListener: SettingsListener, + val splitTunnelling: SplitTunnelling ) { val accountCache = AccountCache(daemon, settingsListener) val keyStatusListener = KeyStatusListener(daemon) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SplitTunnelling.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SplitTunnelling.kt new file mode 100644 index 0000000000..0722c2fc56 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SplitTunnelling.kt @@ -0,0 +1,33 @@ +package net.mullvad.mullvadvpn.service + +import kotlin.properties.Delegates.observable + +class SplitTunnelling { + private val excludedApps = HashSet<String>() + + val excludedAppList + get() = if (enabled) { + excludedApps.toList() + } else { + emptyList() + } + + var enabled by observable(false) { _, _, _ -> update() } + var onChange: ((List<String>) -> Unit)? = null + + fun isAppExcluded(appPackageName: String) = excludedApps.contains(appPackageName) + + fun excludeApp(appPackageName: String) { + excludedApps.add(appPackageName) + update() + } + + fun includeApp(appPackageName: String) { + excludedApps.remove(appPackageName) + update() + } + + private fun update() { + onChange?.invoke(excludedAppList) + } +} diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt index b2717fb1ec..43ecb29ae2 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt @@ -12,6 +12,7 @@ class ServiceConnection(private val service: ServiceInstance, val mainActivity: val keyStatusListener = service.keyStatusListener val locationInfoCache = service.locationInfoCache val settingsListener = service.settingsListener + val splitTunnelling = service.splitTunnelling val appVersionInfoCache = AppVersionInfoCache(mainActivity, daemon, settingsListener) var relayListListener = RelayListListener(daemon, settingsListener) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt index 03dbd53f0d..29577058b8 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt @@ -13,6 +13,7 @@ import net.mullvad.mullvadvpn.service.KeyStatusListener import net.mullvad.mullvadvpn.service.LocationInfoCache import net.mullvad.mullvadvpn.service.MullvadDaemon import net.mullvad.mullvadvpn.service.SettingsListener +import net.mullvad.mullvadvpn.service.SplitTunnelling import net.mullvad.talpid.ConnectivityListener abstract class ServiceDependentFragment(val onNoService: OnNoService) : ServiceAwareFragment() { @@ -58,6 +59,8 @@ abstract class ServiceDependentFragment(val onNoService: OnNoService) : ServiceA lateinit var settingsListener: SettingsListener private set + lateinit var splitTunnelling: SplitTunnelling + override fun onNewServiceConnection(serviceConnection: ServiceConnection) { // This method is always either called first or after an `onNoServiceConnection`, so the // initialization of the fields doesn't have to be synchronized @@ -70,6 +73,7 @@ abstract class ServiceDependentFragment(val onNoService: OnNoService) : ServiceA locationInfoCache = serviceConnection.locationInfoCache relayListListener = serviceConnection.relayListListener settingsListener = serviceConnection.settingsListener + splitTunnelling = serviceConnection.splitTunnelling synchronized(this) { when (state) { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt index e00bda1d5e..b94883798d 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt @@ -52,7 +52,7 @@ class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchS override fun onAttach(context: Context) { super.onAttach(context) - appListAdapter = AppListAdapter(context) + appListAdapter = AppListAdapter(context, splitTunnelling) } override fun onSafelyCreateView( @@ -133,10 +133,12 @@ class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchS excludeApplications.visibility = View.VISIBLE excludeApplicationsFadeOut.reverse() + splitTunnelling.enabled = true } private fun disable() { appListAdapter.enabled = false + splitTunnelling.enabled = false excludeApplicationsFadeOut.start() } diff --git a/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt index 8f095641a1..3ad9809928 100644 --- a/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt +++ b/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt @@ -16,6 +16,9 @@ open class TalpidVpnService : VpnService() { } private var currentTunConfig = defaultTunConfig() + private var tunIsStale = false + + protected var disallowedApps: List<String>? = null val connectivityListener = ConnectivityListener() @@ -31,13 +34,14 @@ open class TalpidVpnService : VpnService() { synchronized(this) { val tunDevice = activeTunDevice - if (config == currentTunConfig && tunDevice != null) { + if (config == currentTunConfig && tunDevice != null && !tunIsStale) { return tunDevice } else { val newTunDevice = createTun(config) currentTunConfig = config activeTunDevice = newTunDevice + tunIsStale = false return newTunDevice } @@ -73,6 +77,12 @@ open class TalpidVpnService : VpnService() { } } + fun markTunAsStale() { + synchronized(this) { + tunIsStale = true + } + } + private fun createTun(config: TunConfig): Int { if (VpnService.prepare(this) != null) { // VPN permission wasn't granted @@ -92,6 +102,12 @@ open class TalpidVpnService : VpnService() { addRoute(route.address, route.prefixLength.toInt()) } + disallowedApps?.let { apps -> + for (app in apps) { + addDisallowedApplication(app) + } + } + setMtu(config.mtu) setBlocking(false) } |
