diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-05-23 09:37:21 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-05-23 09:37:21 -0300 |
| commit | 8ab37040fcc2e8ec0e70555608351e9f4aa01e2c (patch) | |
| tree | 4e6f599959aaabc8281d64d0967681c45d947533 | |
| parent | a995adbc781a4afd7142cb4d22fe6edffbe20607 (diff) | |
| parent | 3e8c45e2b8d270ec1e46a0487212e741c0f6db02 (diff) | |
| download | mullvadvpn-8ab37040fcc2e8ec0e70555608351e9f4aa01e2c.tar.xz mullvadvpn-8ab37040fcc2e8ec0e70555608351e9f4aa01e2c.zip | |
Merge branch 'set-relay-constraint-on-android'
16 files changed, 270 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock index 0744c310ca..ccf2d2491f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -698,7 +698,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jni" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1166,7 +1166,7 @@ version = "0.1.0" dependencies = [ "err-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jni 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2847,7 +2847,7 @@ dependencies = [ "checksum ipnetwork 0.14.0 (git+https://github.com/mullvad/ipnetwork?branch=fix-deserialization)" = "<none>" "checksum ipnetwork 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3d862c86f7867f19b693ec86765e0252d82e53d4240b9b629815675a0714ad1" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" -"checksum jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "294eca097d1dc0bf59de5ab9f7eafa5f77129e9f6464c957ed3ddeb705fb4292" +"checksum jni 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6a88aedffe8ad596237d23a363a53a6c7c6493d4918864d47673e6a15eb938b" "checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" "checksum jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)" = "<none>" "checksum jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f29cb249837420fb0cee7fb0fbf1d22679e121b160e71bb5e0d90b9df241c23e" diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt index b01bccb5a4..6d084e6274 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn import net.mullvad.mullvadvpn.model.AccountData import net.mullvad.mullvadvpn.model.RelayList +import net.mullvad.mullvadvpn.model.RelaySettingsUpdate import net.mullvad.mullvadvpn.model.Settings class MullvadDaemon { @@ -14,6 +15,7 @@ class MullvadDaemon { external fun getRelayLocations(): RelayList external fun getSettings(): Settings external fun setAccount(accountToken: String?) + external fun updateRelaySettings(update: RelaySettingsUpdate) private external fun initialize() } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt index 7b1673c448..5ca767c19e 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt @@ -1,5 +1,9 @@ package net.mullvad.mullvadvpn +import kotlinx.coroutines.launch +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope + import android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.widget.LinearLayoutManager @@ -9,6 +13,10 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageButton +import net.mullvad.mullvadvpn.model.Constraint +import net.mullvad.mullvadvpn.model.LocationConstraint +import net.mullvad.mullvadvpn.model.RelaySettingsUpdate +import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.RelayItemDividerDecoration import net.mullvad.mullvadvpn.relaylist.RelayList import net.mullvad.mullvadvpn.relaylist.RelayListAdapter @@ -39,6 +47,7 @@ class SelectLocationFragment : Fragment() { relayListAdapter.onSelect = { relayItem -> parentActivity.selectedRelayItem = relayItem + updateLocationConstraint(relayItem) close() } @@ -49,4 +58,20 @@ class SelectLocationFragment : Fragment() { addItemDecoration(RelayItemDividerDecoration(context!!)) } } + + private fun updateLocationConstraint(relayItem: RelayItem?) = + GlobalScope.launch(Dispatchers.Default) { + val parentActivity = activity as MainActivity + var constraint: Constraint<LocationConstraint> + + if (relayItem == null) { + constraint = Constraint.Any() + } else { + constraint = Constraint.Only(relayItem.location) + } + + parentActivity.asyncDaemon.await().updateRelaySettings( + RelaySettingsUpdate.RelayConstraintsUpdate(constraint) + ) + } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Constraint.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Constraint.kt new file mode 100644 index 0000000000..97b3414176 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Constraint.kt @@ -0,0 +1,6 @@ +package net.mullvad.mullvadvpn.model + +sealed class Constraint<T>() { + class Any<T>() : Constraint<T>() + class Only<T>(val value: T) : Constraint<T>() +} diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/LocationConstraint.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/LocationConstraint.kt new file mode 100644 index 0000000000..4fc07a4b43 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/LocationConstraint.kt @@ -0,0 +1,11 @@ +package net.mullvad.mullvadvpn.model + +sealed class LocationConstraint(val code: Array<String>) { + class Country(var countryCode: String) : LocationConstraint(arrayOf(countryCode)) + + class City(var countryCode: String, var cityCode: String) + : LocationConstraint(arrayOf(countryCode, cityCode)) + + class Hostname(var countryCode: String, var cityCode: String, var hostname: String) + : LocationConstraint(arrayOf(countryCode, cityCode, hostname)) +} diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettingsUpdate.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettingsUpdate.kt new file mode 100644 index 0000000000..aec094fe09 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettingsUpdate.kt @@ -0,0 +1,6 @@ +package net.mullvad.mullvadvpn.model + +sealed class RelaySettingsUpdate { + class CustomTunnelEndpoint() : RelaySettingsUpdate() + class RelayConstraintsUpdate(var location: Constraint<LocationConstraint>?) : RelaySettingsUpdate() +} diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt index 131f71df9d..7e2f09e289 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt @@ -1,8 +1,15 @@ package net.mullvad.mullvadvpn.relaylist -data class Relay(override val name: String) : RelayItem { +import net.mullvad.mullvadvpn.model.LocationConstraint + +data class Relay( + val countryCode: String, + val cityCode: String, + override val name: String +) : RelayItem { override val code = name override val type = RelayItemType.Relay + override val location = LocationConstraint.Hostname(countryCode, cityCode, name) override val hasChildren = false override val visibleChildCount = 0 diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt index a5e1d5cce8..15c52fce43 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt @@ -1,12 +1,17 @@ package net.mullvad.mullvadvpn.relaylist +import net.mullvad.mullvadvpn.model.LocationConstraint + class RelayCity( override val name: String, + val countryCode: String, override val code: String, override var expanded: Boolean, val relays: List<Relay> ) : RelayItem { override val type = RelayItemType.City + override val location = LocationConstraint.City(countryCode, code) + override val hasChildren get() = relays.size > 1 diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt index 80b63875d5..fb2aacab33 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt @@ -1,5 +1,7 @@ package net.mullvad.mullvadvpn.relaylist +import net.mullvad.mullvadvpn.model.LocationConstraint + class RelayCountry( override val name: String, override val code: String, @@ -7,6 +9,8 @@ class RelayCountry( val cities: List<RelayCity> ) : RelayItem { override val type = RelayItemType.Country + override val location = LocationConstraint.Country(code) + override val hasChildren get() = getRelayCount() > 1 diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt index 9f325e6377..22f39cbf7e 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt @@ -1,9 +1,12 @@ package net.mullvad.mullvadvpn.relaylist +import net.mullvad.mullvadvpn.model.LocationConstraint + interface RelayItem { val type: RelayItemType val name: String val code: String + val location: LocationConstraint val hasChildren: Boolean val visibleChildCount: Int diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt index a70ac13eaa..964833f675 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt @@ -6,9 +6,11 @@ class RelayList { constructor(model: net.mullvad.mullvadvpn.model.RelayList) { countries = model.countries.map { country -> val cities = country.cities.map { city -> - val relays = city.relays.map { relay -> Relay(relay.hostname) } + val relays = city.relays.map { relay -> + Relay(country.code, city.code, relay.hostname) + } - RelayCity(city.name, city.code, false, relays) + RelayCity(city.name, country.code, city.code, false, relays) } RelayCountry(country.name, country.code, false, cities) diff --git a/mullvad-jni/Cargo.toml b/mullvad-jni/Cargo.toml index a9b8092d92..bfb4cd7ede 100644 --- a/mullvad-jni/Cargo.toml +++ b/mullvad-jni/Cargo.toml @@ -12,7 +12,7 @@ crate_type = ["cdylib"] [dependencies] err-derive = "0.1.5" futures = "0.1" -jni = "0.11" +jni = "0.12" jsonrpc-client-core = "0.5" lazy_static = "1" log = "0.4" diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs index e0ffa13c84..328d0c9093 100644 --- a/mullvad-jni/src/daemon_interface.rs +++ b/mullvad-jni/src/daemon_interface.rs @@ -1,6 +1,9 @@ use futures::{sync::oneshot, Future}; use mullvad_daemon::{DaemonCommandSender, ManagementCommand}; -use mullvad_types::{account::AccountData, relay_list::RelayList, settings::Settings}; +use mullvad_types::{ + account::AccountData, relay_constraints::RelaySettingsUpdate, relay_list::RelayList, + settings::Settings, +}; #[derive(Debug, err_derive::Error)] pub enum Error { @@ -69,6 +72,14 @@ impl DaemonInterface { rx.wait().map_err(|_| Error::NoResponse) } + pub fn update_relay_settings(&self, update: RelaySettingsUpdate) -> Result<()> { + let (tx, rx) = oneshot::channel(); + + self.send_command(ManagementCommand::UpdateRelaySettings(tx, update))?; + + rx.wait().map_err(|_| Error::NoResponse) + } + fn send_command(&self, command: ManagementCommand) -> Result<()> { let sender = self.command_sender.as_ref().ok_or(Error::NoSender)?; diff --git a/mullvad-jni/src/from_java.rs b/mullvad-jni/src/from_java.rs index 11b98ad753..3acd9717ef 100644 --- a/mullvad-jni/src/from_java.rs +++ b/mullvad-jni/src/from_java.rs @@ -1,8 +1,12 @@ +use crate::{get_class, is_null::IsNull}; use jni::{ objects::{JObject, JString}, JNIEnv, }; -use std::ops::Deref; +use mullvad_types::relay_constraints::{ + Constraint, LocationConstraint, RelayConstraintsUpdate, RelaySettingsUpdate, +}; +use std::fmt::Debug; pub trait FromJava<'env> { type JavaType: 'env; @@ -13,7 +17,7 @@ pub trait FromJava<'env> { impl<'env, T> FromJava<'env> for Option<T> where T: FromJava<'env>, - T::JavaType: Deref<Target = JObject<'env>>, + T::JavaType: IsNull, { type JavaType = T::JavaType; @@ -36,3 +40,132 @@ impl<'env> FromJava<'env> for String { ) } } + +impl<'env, T> FromJava<'env> for Constraint<T> +where + T: Clone + Debug + Eq + FromJava<'env>, + T::JavaType: From<JObject<'env>>, +{ + type JavaType = JObject<'env>; + + fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self { + if is_instance_of(env, source, "net/mullvad/mullvadvpn/model/Constraint$Any") { + Constraint::Any + } else if is_instance_of(env, source, "net/mullvad/mullvadvpn/model/Constraint$Only") { + let value = get_object_field(env, source, "value", "Ljava/lang/Object;"); + + Constraint::Only(T::from_java(env, T::JavaType::from(value))) + } else { + panic!("Invalid Constraint Java sub-class"); + } + } +} + +impl<'env> FromJava<'env> for LocationConstraint { + type JavaType = JObject<'env>; + + fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self { + let country_class = "net/mullvad/mullvadvpn/model/LocationConstraint$Country"; + let city_class = "net/mullvad/mullvadvpn/model/LocationConstraint$City"; + let hostname_class = "net/mullvad/mullvadvpn/model/LocationConstraint$Hostname"; + + if is_instance_of(env, source, country_class) { + let country = get_string_field(env, source, "countryCode"); + + LocationConstraint::Country(String::from_java(env, country)) + } else if is_instance_of(env, source, city_class) { + let country = get_string_field(env, source, "countryCode"); + let city = get_string_field(env, source, "cityCode"); + + LocationConstraint::City( + String::from_java(env, country), + String::from_java(env, city), + ) + } else if is_instance_of(env, source, hostname_class) { + let country = get_string_field(env, source, "countryCode"); + let city = get_string_field(env, source, "cityCode"); + let hostname = get_string_field(env, source, "hostname"); + + LocationConstraint::Hostname( + String::from_java(env, country), + String::from_java(env, city), + String::from_java(env, hostname), + ) + } else { + panic!("Invalid LocationConstraint Java sub-class"); + } + } +} + +impl<'env> FromJava<'env> for RelayConstraintsUpdate { + type JavaType = JObject<'env>; + + fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self { + let location = get_object_field( + env, + source, + "location", + "Lnet/mullvad/mullvadvpn/model/Constraint;", + ); + + RelayConstraintsUpdate { + location: FromJava::from_java(env, location), + tunnel: None, + } + } +} + +impl<'env> FromJava<'env> for RelaySettingsUpdate { + type JavaType = JObject<'env>; + + fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self { + let custom_tunnel_endpoint_class = + "net/mullvad/mullvadvpn/model/RelaySettingsUpdate$CustomTunnelEndpoint"; + let relay_constraints_update_class = + "net/mullvad/mullvadvpn/model/RelaySettingsUpdate$RelayConstraintsUpdate"; + + if is_instance_of(env, source, custom_tunnel_endpoint_class) { + unimplemented!("Can't specify custom tunnels from Android app"); + } else if is_instance_of(env, source, relay_constraints_update_class) { + RelaySettingsUpdate::Normal(RelayConstraintsUpdate::from_java(env, source)) + } else { + panic!("Invalid RelaySettingsUpdate Java sub-class"); + } + } +} + +fn is_instance_of<'env>( + env: &JNIEnv<'env>, + object: JObject<'env>, + class_name: &'static str, +) -> bool { + let class = get_class(class_name); + + env.is_instance_of(object, &class) + .expect("Failed to check if an object is an instance of a specified class") +} + +fn get_string_field<'env>( + env: &JNIEnv<'env>, + object: JObject<'env>, + field_name: &str, +) -> JString<'env> { + JString::from(get_object_field( + env, + object, + field_name, + "Ljava/lang/String;", + )) +} + +fn get_object_field<'env>( + env: &JNIEnv<'env>, + object: JObject<'env>, + field_name: &str, + field_type: &str, +) -> JObject<'env> { + env.get_field(object, field_name, field_type) + .expect("Failed to get field from object") + .l() + .expect("Field has incorrect Java type") +} diff --git a/mullvad-jni/src/is_null.rs b/mullvad-jni/src/is_null.rs new file mode 100644 index 0000000000..628fc4c81a --- /dev/null +++ b/mullvad-jni/src/is_null.rs @@ -0,0 +1,18 @@ +use jni::objects::{JObject, JString}; +use std::ops::Deref; + +pub trait IsNull { + fn is_null(&self) -> bool; +} + +impl<'a> IsNull for JObject<'a> { + fn is_null(&self) -> bool { + self.deref().is_null() + } +} + +impl<'a> IsNull for JString<'a> { + fn is_null(&self) -> bool { + self.deref().is_null() + } +} diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs index 95fccfb5ce..99a3041d9a 100644 --- a/mullvad-jni/src/lib.rs +++ b/mullvad-jni/src/lib.rs @@ -3,6 +3,7 @@ mod daemon_interface; mod from_java; mod into_java; +mod is_null; use crate::{daemon_interface::DaemonInterface, from_java::FromJava, into_java::IntoJava}; use jni::{ @@ -21,10 +22,17 @@ const LOG_FILENAME: &str = "daemon.log"; const CLASSES_TO_LOAD: &[&str] = &[ "java/util/ArrayList", "net/mullvad/mullvadvpn/model/AccountData", + "net/mullvad/mullvadvpn/model/Constraint$Any", + "net/mullvad/mullvadvpn/model/Constraint$Only", + "net/mullvad/mullvadvpn/model/LocationConstraint$City", + "net/mullvad/mullvadvpn/model/LocationConstraint$Country", + "net/mullvad/mullvadvpn/model/LocationConstraint$Hostname", "net/mullvad/mullvadvpn/model/Relay", "net/mullvad/mullvadvpn/model/RelayList", "net/mullvad/mullvadvpn/model/RelayListCity", "net/mullvad/mullvadvpn/model/RelayListCountry", + "net/mullvad/mullvadvpn/model/RelaySettingsUpdate$CustomTunnelEndpoint", + "net/mullvad/mullvadvpn/model/RelaySettingsUpdate$RelayConstraintsUpdate", "net/mullvad/mullvadvpn/model/Settings", ]; @@ -223,3 +231,22 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_setAccount( log::error!("{}", error.display_chain_with_msg("Failed to set account")); } } + +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_updateRelaySettings( + env: JNIEnv, + _: JObject, + relaySettingsUpdate: JObject, +) { + let daemon = DAEMON_INTERFACE.lock(); + + let update = FromJava::from_java(&env, relaySettingsUpdate); + + if let Err(error) = daemon.update_relay_settings(update) { + log::error!( + "{}", + error.display_chain_with_msg("Failed to update relay settings") + ); + } +} |
