summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-08-03 12:15:14 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-08-03 12:15:14 -0300
commit2b5dc52e657786f2d4ecaa362da267f0408d9a64 (patch)
treedc1203de56f3bc562d8ee80dac63b654d62303fc
parentfc28a749fba82bf2fed844a7ffab33f3619f7233 (diff)
parentd20d24bdbce33e4cacebeecec0d518ffbea6a2e9 (diff)
downloadmullvadvpn-2b5dc52e657786f2d4ecaa362da267f0408d9a64.tar.xz
mullvadvpn-2b5dc52e657786f2d4ecaa362da267f0408d9a64.zip
Merge branch 'show-in-out-ip-address'
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt31
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/LocationInfo.kt135
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt27
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/Endpoint.kt5
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/GeoIpLocation.kt10
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/TransportProtocol.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelEndpoint.kt3
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt4
-rw-r--r--android/src/main/res/layout/connect.xml74
-rw-r--r--android/src/main/res/values/strings.xml5
-rw-r--r--mullvad-jni/src/into_java.rs250
-rw-r--r--mullvad-jni/src/lib.rs5
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", &parameters)
+ .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",
+ &parameters,
+ )
+ .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",
+ &parameters,
+ )
+ .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",
&parameters,
)
.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, &parameters)
+ }
+ 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, &parameters)
+ }
+ 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, &parameters)
+ }
+ 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, &parameters)
+ env.new_object(&class, signature, &parameters)
}
- 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",