diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-08-03 12:15:14 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-08-03 12:15:14 -0300 |
| commit | 2b5dc52e657786f2d4ecaa362da267f0408d9a64 (patch) | |
| tree | dc1203de56f3bc562d8ee80dac63b654d62303fc | |
| parent | fc28a749fba82bf2fed844a7ffab33f3619f7233 (diff) | |
| parent | d20d24bdbce33e4cacebeecec0d518ffbea6a2e9 (diff) | |
| download | mullvadvpn-2b5dc52e657786f2d4ecaa362da267f0408d9a64.tar.xz mullvadvpn-2b5dc52e657786f2d4ecaa362da267f0408d9a64.zip | |
Merge branch 'show-in-out-ip-address'
13 files changed, 434 insertions, 123 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt index e9713fff60..a0c94873f4 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt @@ -21,6 +21,8 @@ import net.mullvad.mullvadvpn.dataproxy.RelayListListener import net.mullvad.mullvadvpn.model.KeygenEvent import net.mullvad.mullvadvpn.model.TunnelState +val KEY_IS_TUNNEL_INFO_EXPANDED = "is_tunnel_info_expanded" + class ConnectFragment : Fragment() { private lateinit var actionButton: ConnectActionButton private lateinit var switchLocationButton: SwitchLocationButton @@ -39,6 +41,8 @@ class ConnectFragment : Fragment() { private lateinit var updateKeyStatusJob: Job private lateinit var updateTunnelStateJob: Job + private var isTunnelInfoExpanded = false + override fun onAttach(context: Context) { super.onAttach(context) @@ -50,6 +54,13 @@ class ConnectFragment : Fragment() { versionInfoCache = parentActivity.appVersionInfoCache } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + isTunnelInfoExpanded = + savedInstanceState?.getBoolean(KEY_IS_TUNNEL_INFO_EXPANDED, false) ?: false + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -64,7 +75,9 @@ class ConnectFragment : Fragment() { headerBar = HeaderBar(view, context!!) notificationBanner = NotificationBanner(view, context!!, versionInfoCache) status = ConnectionStatus(view, context!!) - locationInfo = LocationInfo(view, locationInfoCache) + + locationInfo = LocationInfo(view, context!!) + locationInfo.isTunnelInfoExpanded = isTunnelInfoExpanded actionButton = ConnectActionButton(view) actionButton.apply { @@ -90,11 +103,17 @@ class ConnectFragment : Fragment() { override fun onResume() { super.onResume() + locationInfo.isTunnelInfoExpanded = isTunnelInfoExpanded + keyStatusListener.onKeyStatusChange = { keyStatus -> updateKeyStatusJob.cancel() updateKeyStatusJob = updateKeyStatus(keyStatus) } + locationInfoCache.onNewLocation = { location -> + locationInfo.location = location + } + relayListListener.onRelayListChange = { relayList, selectedRelayItem -> switchLocationButton.location = selectedRelayItem } @@ -102,13 +121,15 @@ class ConnectFragment : Fragment() { override fun onPause() { keyStatusListener.onKeyStatusChange = null + locationInfoCache.onNewLocation = null relayListListener.onRelayListChange = null + isTunnelInfoExpanded = locationInfo.isTunnelInfoExpanded + super.onPause() } override fun onDestroyView() { - locationInfo.onDestroy() switchLocationButton.onDestroy() connectionProxy.onUiStateChange = null @@ -117,10 +138,16 @@ class ConnectFragment : Fragment() { super.onDestroyView() } + override fun onSaveInstanceState(state: Bundle) { + isTunnelInfoExpanded = locationInfo.isTunnelInfoExpanded + state.putBoolean(KEY_IS_TUNNEL_INFO_EXPANDED, isTunnelInfoExpanded) + } + private fun updateTunnelState(uiState: TunnelState) = GlobalScope.launch(Dispatchers.Main) { val realState = connectionProxy.state locationInfoCache.state = realState + locationInfo.state = realState headerBar.setState(realState) actionButton.tunnelState = uiState diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/LocationInfo.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/LocationInfo.kt index f1fb1d7860..68e5840550 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/LocationInfo.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/LocationInfo.kt @@ -1,28 +1,137 @@ package net.mullvad.mullvadvpn +import android.content.Context import android.view.View import android.widget.TextView -import net.mullvad.mullvadvpn.dataproxy.LocationInfoCache +import net.mullvad.mullvadvpn.model.Endpoint +import net.mullvad.mullvadvpn.model.GeoIpLocation +import net.mullvad.mullvadvpn.model.TransportProtocol +import net.mullvad.mullvadvpn.model.TunnelState -class LocationInfo(val parentView: View, val locationInfoCache: LocationInfoCache) { - private val countryLabel: TextView = parentView.findViewById(R.id.country) - private val cityLabel: TextView = parentView.findViewById(R.id.city) - private val hostnameLabel: TextView = parentView.findViewById(R.id.hostname) +class LocationInfo(val parentView: View, val context: Context) { + private val country: TextView = parentView.findViewById(R.id.country) + private val city: TextView = parentView.findViewById(R.id.city) + private val tunnelInfo: View = parentView.findViewById(R.id.tunnel_info) + private val hostname: TextView = parentView.findViewById(R.id.hostname) + private val chevron: View = parentView.findViewById(R.id.chevron) + private val protocol: TextView = parentView.findViewById(R.id.tunnel_protocol) + private val inAddress: TextView = parentView.findViewById(R.id.in_address) + private val outAddress: TextView = parentView.findViewById(R.id.out_address) + + private var endpoint: Endpoint? = null + private var isTunnelInfoVisible = false + var isTunnelInfoExpanded = false + + var location: GeoIpLocation? = null + set(value) { + country.text = value?.country ?: "" + city.text = value?.city ?: "" + hostname.text = value?.hostname ?: "" + + updateOutAddress(value) + } + + var state: TunnelState = TunnelState.Disconnected() + set(value) { + field = value + + when (value) { + is TunnelState.Connecting -> { + endpoint = value.endpoint?.endpoint + isTunnelInfoVisible = true + } + is TunnelState.Connected -> { + endpoint = value.endpoint.endpoint + isTunnelInfoVisible = true + } + else -> { + endpoint = null + isTunnelInfoVisible = false + } + } + + updateTunnelInfo() + } init { - locationInfoCache.onNewLocation = { country, city, hostname -> - updateViews(country, city, hostname) + tunnelInfo.setOnClickListener { toggleTunnelInfo() } + } + + private fun toggleTunnelInfo() { + isTunnelInfoExpanded = !isTunnelInfoExpanded + updateTunnelInfo() + } + + private fun updateTunnelInfo() { + if (isTunnelInfoVisible) { + showTunnelInfo() + } else { + hideTunnelInfo() } } - fun onDestroy() { - locationInfoCache.onNewLocation = null + private fun hideTunnelInfo() { + chevron.visibility = View.INVISIBLE + + protocol.text = "" + inAddress.text = "" + outAddress.text = "" } - fun updateViews(country: String, city: String, hostname: String) { - countryLabel.text = country - cityLabel.text = city - hostnameLabel.text = hostname + private fun showTunnelInfo() { + chevron.visibility = View.VISIBLE + + if (isTunnelInfoExpanded) { + chevron.rotation = 180.0F + protocol.setText(R.string.wireguard) + showInAddress(endpoint) + updateOutAddress(location) + } else { + chevron.rotation = 0.0F + protocol.text = "" + inAddress.text = "" + outAddress.text = "" + } + } + + private fun showInAddress(endpoint: Endpoint?) { + if (endpoint != null) { + val transportProtocol = when (endpoint.protocol) { + is TransportProtocol.Tcp -> context.getString(R.string.tcp) + is TransportProtocol.Udp -> context.getString(R.string.udp) + } + + inAddress.text = context.getString( + R.string.in_address, + endpoint.address.address.hostAddress, + endpoint.address.port, + transportProtocol + ) + } else { + inAddress.text = "" + } + } + + private fun updateOutAddress(location: GeoIpLocation?) { + val addressAvailable = location != null && (location.ipv4 != null || location.ipv6 != null) + + if (isTunnelInfoVisible && addressAvailable && isTunnelInfoExpanded) { + val ipv4 = location!!.ipv4 + val ipv6 = location.ipv6 + val ipAddress: String + + if (ipv6 == null) { + ipAddress = ipv4!!.hostAddress + } else if (ipv4 == null) { + ipAddress = ipv6.hostAddress + } else { + ipAddress = "${ipv4.hostAddress} / ${ipv6.hostAddress}" + } + + outAddress.text = context.getString(R.string.out_address, ipAddress) + } else { + outAddress.text = "" + } } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt index 17c5dff47d..b8b0806285 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt @@ -81,7 +81,7 @@ class ConnectionProxy(val parentActivity: MainActivity) { if (currentState is TunnelState.Connecting || currentState is TunnelState.Connected) { return false } else { - uiState = TunnelState.Connecting(null) + uiState = TunnelState.Connecting(null, null) return true } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt index 6fc655c2a8..2cfa32883b 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt @@ -22,16 +22,16 @@ class LocationInfoCache( private var lastKnownRealLocation: GeoIpLocation? = null private var activeFetch: Job? = null - var onNewLocation: ((String, String, String) -> Unit)? = null + var onNewLocation: ((GeoIpLocation?) -> Unit)? = null set(value) { field = value - notifyNewLocation() + value?.invoke(location) } var location: GeoIpLocation? = null set(value) { field = value - notifyNewLocation() + onNewLocation?.invoke(value) } var state: TunnelState = TunnelState.Disconnected() @@ -59,22 +59,21 @@ class LocationInfoCache( } } - fun notifyNewLocation() { - val location = this.location - val country = location?.country ?: "" - val city = location?.city ?: "" - val hostname = location?.hostname ?: "" - - onNewLocation?.invoke(country, city, hostname) - } - private fun locationFromSelectedRelay(): GeoIpLocation? { val relayItem = relayListListener.selectedRelayItem when (relayItem) { - is RelayCountry -> return GeoIpLocation(relayItem.name, null, null) - is RelayCity -> return GeoIpLocation(relayItem.country.name, relayItem.name, null) + is RelayCountry -> return GeoIpLocation(null, null, relayItem.name, null, null) + is RelayCity -> return GeoIpLocation( + null, + null, + relayItem.country.name, + relayItem.name, + null + ) is Relay -> return GeoIpLocation( + null, + null, relayItem.city.country.name, relayItem.city.name, relayItem.name diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Endpoint.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Endpoint.kt new file mode 100644 index 0000000000..bf1ca2cad4 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Endpoint.kt @@ -0,0 +1,5 @@ +package net.mullvad.mullvadvpn.model + +import java.net.InetSocketAddress + +data class Endpoint(val address: InetSocketAddress, val protocol: TransportProtocol) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/GeoIpLocation.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/GeoIpLocation.kt index ed7ccb7e66..1f4acef73f 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/GeoIpLocation.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/GeoIpLocation.kt @@ -1,3 +1,11 @@ package net.mullvad.mullvadvpn.model -data class GeoIpLocation(val country: String, val city: String?, val hostname: String?) +import java.net.InetAddress + +data class GeoIpLocation( + val ipv4: InetAddress?, + val ipv6: InetAddress?, + val country: String, + val city: String?, + val hostname: String? +) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TransportProtocol.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TransportProtocol.kt new file mode 100644 index 0000000000..185461cda7 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TransportProtocol.kt @@ -0,0 +1,6 @@ +package net.mullvad.mullvadvpn.model + +sealed class TransportProtocol { + class Tcp : TransportProtocol() + class Udp : TransportProtocol() +} diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelEndpoint.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelEndpoint.kt new file mode 100644 index 0000000000..f064218215 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelEndpoint.kt @@ -0,0 +1,3 @@ +package net.mullvad.mullvadvpn.model + +data class TunnelEndpoint(val endpoint: Endpoint) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt index 054a07ed4d..33a40f7196 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt @@ -2,8 +2,8 @@ package net.mullvad.mullvadvpn.model sealed class TunnelState() { class Disconnected() : TunnelState() - class Connecting(val location: GeoIpLocation?) : TunnelState() - class Connected(val location: GeoIpLocation?) : TunnelState() + class Connecting(val endpoint: TunnelEndpoint?, val location: GeoIpLocation?) : TunnelState() + class Connected(val endpoint: TunnelEndpoint, val location: GeoIpLocation?) : TunnelState() class Disconnecting(val actionAfterDisconnect: ActionAfterDisconnect) : TunnelState() class Blocked(val reason: BlockReason) : TunnelState() } diff --git a/android/src/main/res/layout/connect.xml b/android/src/main/res/layout/connect.xml index 4468e838e8..579e3c6a4e 100644 --- a/android/src/main/res/layout/connect.xml +++ b/android/src/main/res/layout/connect.xml @@ -137,13 +137,13 @@ android:layout_height="wrap_content" android:layout_weight="0" android:orientation="vertical" - android:padding="24dp" android:gravity="start" > <TextView android:id="@+id/connection_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="4dp" + android:layout_marginHorizontal="24dp" android:textColor="@color/red" android:textSize="16sp" android:textStyle="bold" @@ -153,6 +153,7 @@ <TextView android:id="@+id/country" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" android:textColor="@color/white" android:textSize="34sp" android:textStyle="bold" @@ -161,26 +162,69 @@ <TextView android:id="@+id/city" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" android:textColor="@color/white" android:textSize="34sp" android:textStyle="bold" android:text="" /> - <TextView android:id="@+id/hostname" - android:layout_width="wrap_content" + + <LinearLayout android:id="@+id/tunnel_info" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:textColor="@color/white" - android:textSize="16sp" - android:textStyle="bold" - android:text="" - /> - </LinearLayout> + android:layout_weight="0" + android:orientation="vertical" + android:paddingHorizontal="24dp" + android:gravity="start" + android:clickable="true" + android:background="?android:attr/selectableItemBackground" + > + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + <TextView android:id="@+id/hostname" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/white" + android:textSize="16sp" + android:textStyle="bold" + android:text="" + /> + <ImageView android:id="@+id/chevron" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginHorizontal="5dp" + android:alpha="0.4" + android:src="@drawable/icon_chevron_expand" + /> + </LinearLayout> - <Space - android:layout_width="match_parent" - android:layout_height="32dp" - android:layout_weight="0" - /> + <TextView android:id="@+id/tunnel_protocol" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textColor="@color/white" + android:textSize="13sp" + android:text="" + /> + <TextView android:id="@+id/in_address" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/white" + android:textSize="13sp" + android:text="" + /> + <TextView android:id="@+id/out_address" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/white" + android:textSize="13sp" + android:text="" + /> + </LinearLayout> + </LinearLayout> <LinearLayout android:layout_width="match_parent" @@ -190,7 +234,7 @@ android:padding="24dp" > <Button android:id="@+id/switch_location" - android:layout_marginVertical="16dp" + android:layout_marginBottom="16dp" android:text="@string/switch_location" android:drawableRight="@drawable/icon_chevron" android:paddingRight="8dp" diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index d419e4abc3..6b605a7bc2 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -60,6 +60,11 @@ <string name="cancel">Cancel</string> <string name="disconnect">Disconnect</string> <string name="switch_location">Switch location</string> + <string name="wireguard">WireGuard</string> + <string name="tcp">TCP</string> + <string name="udp">UDP</string> + <string name="in_address">In %1$s:%2$d %3$s</string> + <string name="out_address">Out %1$s</string> <string name="blocking_internet">Blocking internet</string> <string name="auth_failed">Account authentication failed.</string> diff --git a/mullvad-jni/src/into_java.rs b/mullvad-jni/src/into_java.rs index fe668f1580..7ee3e6fec4 100644 --- a/mullvad-jni/src/into_java.rs +++ b/mullvad-jni/src/into_java.rs @@ -17,10 +17,13 @@ use mullvad_types::{ wireguard::KeygenEvent, CustomTunnelEndpoint, }; -use std::{fmt::Debug, net::IpAddr}; +use std::{ + fmt::Debug, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, +}; use talpid_core::tunnel::tun_provider::TunConfig; use talpid_types::{ - net::wireguard::PublicKey, + net::{wireguard::PublicKey, Endpoint, TransportProtocol, TunnelEndpoint}, tunnel::{ActionAfterDisconnect, BlockReason}, }; @@ -108,56 +111,84 @@ impl<'array, 'env> IntoJava<'env> for &'array [u8] { } } -impl<'env> IntoJava<'env> for IpAddr { +fn ipvx_addr_into_java<'env>(original_octets: &[u8], env: &JNIEnv<'env>) -> JObject<'env> { + let class = get_class("java/net/InetAddress"); + + let constructor = env + .get_static_method_id(&class, "getByAddress", "([B)Ljava/net/InetAddress;") + .expect("Failed to get InetAddress.getByAddress method ID"); + + let octets_array = env + .new_byte_array(original_octets.len() as i32) + .expect("Failed to create byte array to store IP address"); + + let octet_data: Vec<i8> = original_octets + .into_iter() + .map(|octet| *octet as i8) + .collect(); + + env.set_byte_array_region(octets_array, 0, &octet_data) + .expect("Failed to copy IP address octets to byte array"); + + let octets = env.auto_local(JObject::from(octets_array)); + let result = env + .call_static_method_unchecked( + "java/net/InetAddress", + constructor, + JavaType::Object("java/net/InetAddress".to_owned()), + &[JValue::Object(octets.as_obj())], + ) + .expect("Failed to create InetAddress Java object"); + + match result { + JValue::Object(object) => object, + value => { + panic!( + "InetAddress.getByAddress returned an invalid value: {:?}", + value + ); + } + } +} + +impl<'env> IntoJava<'env> for Ipv4Addr { type JavaType = JObject<'env>; fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { - let class = get_class("java/net/InetAddress"); + ipvx_addr_into_java(self.octets().as_ref(), env) + } +} - let constructor = env - .get_static_method_id(&class, "getByAddress", "([B)Ljava/net/InetAddress;") - .expect("Failed to get InetAddress.getByAddress method ID"); +impl<'env> IntoJava<'env> for Ipv6Addr { + type JavaType = JObject<'env>; - let octet_count = if self.is_ipv4() { 4 } else { 16 }; - let octets_array = env - .new_byte_array(octet_count) - .expect("Failed to create byte array to store IP address"); + fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { + ipvx_addr_into_java(self.octets().as_ref(), env) + } +} - let octet_data: Vec<i8> = match self { - IpAddr::V4(address) => address - .octets() - .into_iter() - .map(|octet| *octet as i8) - .collect(), - IpAddr::V6(address) => address - .octets() - .into_iter() - .map(|octet| *octet as i8) - .collect(), - }; +impl<'env> IntoJava<'env> for IpAddr { + type JavaType = JObject<'env>; - env.set_byte_array_region(octets_array, 0, &octet_data) - .expect("Failed to copy IP address octets to byte array"); + fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { + match self { + IpAddr::V4(address) => address.into_java(env), + IpAddr::V6(address) => address.into_java(env), + } + } +} - let octets = env.auto_local(JObject::from(octets_array)); - let result = env - .call_static_method_unchecked( - "java/net/InetAddress", - constructor, - JavaType::Object("java/net/InetAddress".to_owned()), - &[JValue::Object(octets.as_obj())], - ) - .expect("Failed to create InetAddress Java object"); +impl<'env> IntoJava<'env> for SocketAddr { + type JavaType = JObject<'env>; - match result { - JValue::Object(object) => object, - value => { - panic!( - "InetAddress.getByAddress returned an invalid value: {:?}", - value - ); - } - } + fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { + let class = get_class("java/net/InetSocketAddress"); + let ip_address = env.auto_local(self.ip().into_java(env)); + let port = self.port() as jint; + let parameters = [JValue::Object(ip_address.as_obj()), JValue::Int(port)]; + + env.new_object(&class, "(Ljava/net/InetAddress;I)V", ¶meters) + .expect("Failed to create InetSocketAddress Java object") } } @@ -252,15 +283,72 @@ impl<'env> IntoJava<'env> for TunConfig { } } +impl<'env> IntoJava<'env> for TransportProtocol { + type JavaType = JObject<'env>; + + fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { + let class_name = match self { + TransportProtocol::Tcp => "net/mullvad/mullvadvpn/model/TransportProtocol$Tcp", + TransportProtocol::Udp => "net/mullvad/mullvadvpn/model/TransportProtocol$Udp", + }; + let class = get_class(class_name); + + env.new_object(&class, "()V", &[]) + .expect("Failed to create TransportProtocol sub-class variant Java object") + } +} + +impl<'env> IntoJava<'env> for Endpoint { + type JavaType = JObject<'env>; + + fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { + let class = get_class("net/mullvad/mullvadvpn/model/Endpoint"); + let address = env.auto_local(self.address.into_java(env)); + let protocol = env.auto_local(self.protocol.into_java(env)); + let parameters = [ + JValue::Object(address.as_obj()), + JValue::Object(protocol.as_obj()), + ]; + + env.new_object( + &class, + "(Ljava/net/InetSocketAddress;Lnet/mullvad/mullvadvpn/model/TransportProtocol;)V", + ¶meters, + ) + .expect("Failed to create Endpoint sub-class variant Java object") + } +} + +impl<'env> IntoJava<'env> for TunnelEndpoint { + type JavaType = JObject<'env>; + + fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { + let class = get_class("net/mullvad/mullvadvpn/model/TunnelEndpoint"); + let endpoint = env.auto_local(self.endpoint.into_java(env)); + let parameters = [JValue::Object(endpoint.as_obj())]; + + env.new_object( + &class, + "(Lnet/mullvad/mullvadvpn/model/Endpoint;)V", + ¶meters, + ) + .expect("Failed to create TunnelEndpoint sub-class variant Java object") + } +} + impl<'env> IntoJava<'env> for GeoIpLocation { type JavaType = JObject<'env>; fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { let class = get_class("net/mullvad/mullvadvpn/model/GeoIpLocation"); + let ipv4 = env.auto_local(self.ipv4.into_java(env)); + let ipv6 = env.auto_local(self.ipv6.into_java(env)); let country = env.auto_local(JObject::from(self.country.into_java(env))); let city = env.auto_local(JObject::from(self.city.into_java(env))); let hostname = env.auto_local(JObject::from(self.hostname.into_java(env))); let parameters = [ + JValue::Object(ipv4.as_obj()), + JValue::Object(ipv6.as_obj()), JValue::Object(country.as_obj()), JValue::Object(city.as_obj()), JValue::Object(hostname.as_obj()), @@ -268,7 +356,7 @@ impl<'env> IntoJava<'env> for GeoIpLocation { env.new_object( &class, - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + "(Ljava/net/InetAddress;Ljava/net/InetAddress;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", ¶meters, ) .expect("Failed to create GeoIpLocation Java object") @@ -576,42 +664,54 @@ impl<'env> IntoJava<'env> for TunnelState { type JavaType = JObject<'env>; fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType { - let (variant, parameter) = match self { - TunnelState::Disconnected => ("Disconnected", None), - TunnelState::Connecting { location, .. } => ( - "Connecting", - Some((location.into_java(env), "GeoIpLocation")), - ), - TunnelState::Connected { location, .. } => ( - "Connected", - Some((location.into_java(env), "GeoIpLocation")), - ), - TunnelState::Disconnecting(action_after_disconnect) => ( - "Disconnecting", - Some(( - action_after_disconnect.into_java(env), - "ActionAfterDisconnect", - )), - ), - TunnelState::Blocked(reason) => { - ("Blocked", Some((reason.into_java(env), "BlockReason"))) + match self { + TunnelState::Disconnected => { + let class = get_class("net/mullvad/mullvadvpn/model/TunnelState$Disconnected"); + + env.new_object(&class, "()V", &[]) } - }; + TunnelState::Connecting { endpoint, location } => { + let class = get_class("net/mullvad/mullvadvpn/model/TunnelState$Connecting"); + let endpoint = env.auto_local(endpoint.into_java(env)); + let location = env.auto_local(location.into_java(env)); + let parameters = [ + JValue::Object(endpoint.as_obj()), + JValue::Object(location.as_obj()), + ]; + let signature = + "(Lnet/mullvad/mullvadvpn/model/TunnelEndpoint;Lnet/mullvad/mullvadvpn/model/GeoIpLocation;)V"; - let class = get_class(&format!( - "net/mullvad/mullvadvpn/model/TunnelState${}", - variant - )); + env.new_object(&class, signature, ¶meters) + } + TunnelState::Connected { endpoint, location } => { + let class = get_class("net/mullvad/mullvadvpn/model/TunnelState$Connected"); + let endpoint = env.auto_local(endpoint.into_java(env)); + let location = env.auto_local(location.into_java(env)); + let parameters = [ + JValue::Object(endpoint.as_obj()), + JValue::Object(location.as_obj()), + ]; + let signature = + "(Lnet/mullvad/mullvadvpn/model/TunnelEndpoint;Lnet/mullvad/mullvadvpn/model/GeoIpLocation;)V"; + + env.new_object(&class, signature, ¶meters) + } + TunnelState::Disconnecting(action_after_disconnect) => { + let class = get_class("net/mullvad/mullvadvpn/model/TunnelState$Disconnecting"); + let after_disconnect = env.auto_local(action_after_disconnect.into_java(env)); + let parameters = [JValue::Object(after_disconnect.as_obj())]; + let signature = "(Lnet/mullvad/mullvadvpn/model/ActionAfterDisconnect;)V"; - match parameter { - Some((java_object, class_name)) => { - let parameter = env.auto_local(java_object); - let parameters = [JValue::Object(parameter.as_obj())]; - let signature = format!("(Lnet/mullvad/mullvadvpn/model/{};)V", class_name); + env.new_object(&class, signature, ¶meters) + } + TunnelState::Blocked(block_reason) => { + let class = get_class("net/mullvad/mullvadvpn/model/TunnelState$Blocked"); + let reason = env.auto_local(block_reason.into_java(env)); + let parameters = [JValue::Object(reason.as_obj())]; + let signature = "(Lnet/mullvad/mullvadvpn/model/BlockReason;)V"; - env.new_object(&class, &signature, ¶meters) + env.new_object(&class, signature, ¶meters) } - None => env.new_object(&class, "()V", &[]), } .expect("Failed to create TunnelState sub-class variant Java object") } diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs index 96d8c8ba55..28c68e422f 100644 --- a/mullvad-jni/src/lib.rs +++ b/mullvad-jni/src/lib.rs @@ -31,6 +31,7 @@ const LOG_FILENAME: &str = "daemon.log"; const CLASSES_TO_LOAD: &[&str] = &[ "java/net/InetAddress", + "java/net/InetSocketAddress", "java/util/ArrayList", "java/util/List", "net/mullvad/mullvadvpn/model/AccountData", @@ -48,6 +49,7 @@ const CLASSES_TO_LOAD: &[&str] = &[ "net/mullvad/mullvadvpn/model/AppVersionInfo", "net/mullvad/mullvadvpn/model/Constraint$Any", "net/mullvad/mullvadvpn/model/Constraint$Only", + "net/mullvad/mullvadvpn/model/Endpoint", "net/mullvad/mullvadvpn/model/GeoIpLocation", "net/mullvad/mullvadvpn/model/InetNetwork", "net/mullvad/mullvadvpn/model/KeygenEvent$NewKey", @@ -66,7 +68,10 @@ const CLASSES_TO_LOAD: &[&str] = &[ "net/mullvad/mullvadvpn/model/RelaySettingsUpdate$CustomTunnelEndpoint", "net/mullvad/mullvadvpn/model/RelaySettingsUpdate$RelayConstraintsUpdate", "net/mullvad/mullvadvpn/model/Settings", + "net/mullvad/mullvadvpn/model/TransportProtocol$Tcp", + "net/mullvad/mullvadvpn/model/TransportProtocol$Udp", "net/mullvad/mullvadvpn/model/TunConfig", + "net/mullvad/mullvadvpn/model/TunnelEndpoint", "net/mullvad/mullvadvpn/model/TunnelState$Blocked", "net/mullvad/mullvadvpn/model/TunnelState$Connected", "net/mullvad/mullvadvpn/model/TunnelState$Connecting", |
