summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-05-22 16:40:14 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-05-22 16:40:14 -0300
commit11a05dad217354464ce8f47c2b66cb4851e090f1 (patch)
tree2e5aea78112e19f15becc3769b7519963875a478
parent1d9a3f9c6a3bc536de0a92654d87f982ef2bb219 (diff)
parent51eed2f2ed26592141f741d680241d26a79306a4 (diff)
downloadmullvadvpn-11a05dad217354464ce8f47c2b66cb4851e090f1.tar.xz
mullvadvpn-11a05dad217354464ce8f47c2b66cb4851e090f1.zip
Merge branch 'login-on-android'
-rw-r--r--Cargo.lock2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt33
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt4
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountData.kt3
-rw-r--r--mullvad-jni/Cargo.toml2
-rw-r--r--mullvad-jni/src/daemon_interface.rs38
-rw-r--r--mullvad-jni/src/from_java.rs18
-rw-r--r--mullvad-jni/src/into_java.rs33
-rw-r--r--mullvad-jni/src/lib.rs72
9 files changed, 195 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 60bfb33769..0744c310ca 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1165,7 +1165,9 @@ name = "mullvad-jni"
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)",
+ "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)",
"log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
index 9159c93079..3ead35333c 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
@@ -1,5 +1,12 @@
package net.mullvad.mullvadvpn
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Job
+
import android.os.Bundle
import android.os.Handler
import android.support.v4.app.Fragment
@@ -16,6 +23,8 @@ class LoginFragment : Fragment() {
private lateinit var loginFailStatus: View
private lateinit var accountInput: AccountInput
+ private var loginJob: Deferred<Boolean>? = null
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -45,11 +54,27 @@ class LoginFragment : Fragment() {
accountInput.state = LoginState.InProgress
- // TODO: Actually log in
- if ("1234567890".equals(accountToken)) {
- Handler().postDelayed(Runnable { loggedIn() }, 1000)
+ performLogin(accountToken)
+ }
+
+ private fun performLogin(accountToken: String) = GlobalScope.launch(Dispatchers.Main) {
+ loginJob?.cancel()
+ loginJob = GlobalScope.async(Dispatchers.Default) {
+ val parentActivity = activity as MainActivity
+ val daemon = parentActivity.asyncDaemon.await()
+ val accountData = daemon.getAccountData(accountToken)
+
+ if (accountData != null) {
+ true
+ } else {
+ false
+ }
+ }
+
+ if (loginJob?.await() ?: false) {
+ loggedIn()
} else {
- Handler().postDelayed(Runnable { loginFailure() }, 1000)
+ loginFailure()
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
index d5ab845f89..c7025ea644 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
@@ -1,10 +1,14 @@
package net.mullvad.mullvadvpn
+import net.mullvad.mullvadvpn.model.AccountData
+
class MullvadDaemon {
init {
System.loadLibrary("mullvad_jni")
initialize()
}
+ external fun getAccountData(accountToken: String): AccountData?
+
private external fun initialize()
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountData.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountData.kt
new file mode 100644
index 0000000000..6dda6b8352
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountData.kt
@@ -0,0 +1,3 @@
+package net.mullvad.mullvadvpn.model
+
+data class AccountData(val expiry: String)
diff --git a/mullvad-jni/Cargo.toml b/mullvad-jni/Cargo.toml
index 9b87aa07a3..a9b8092d92 100644
--- a/mullvad-jni/Cargo.toml
+++ b/mullvad-jni/Cargo.toml
@@ -11,7 +11,9 @@ crate_type = ["cdylib"]
[dependencies]
err-derive = "0.1.5"
+futures = "0.1"
jni = "0.11"
+jsonrpc-client-core = "0.5"
lazy_static = "1"
log = "0.4"
log-panics = "2"
diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs
index b682660edf..6b0e907eda 100644
--- a/mullvad-jni/src/daemon_interface.rs
+++ b/mullvad-jni/src/daemon_interface.rs
@@ -1,4 +1,23 @@
-use mullvad_daemon::DaemonCommandSender;
+use futures::{sync::oneshot, Future};
+use mullvad_daemon::{DaemonCommandSender, ManagementCommand};
+use mullvad_types::account::AccountData;
+
+#[derive(Debug, err_derive::Error)]
+pub enum Error {
+ #[error(display = "Can't send command to daemon because it is not running")]
+ NoDaemon(#[error(cause)] mullvad_daemon::Error),
+
+ #[error(display = "No response received from daemon")]
+ NoResponse,
+
+ #[error(display = "Attempt to use daemon command sender before it was configured")]
+ NoSender,
+
+ #[error(display = "Error performing RPC with the remote API")]
+ RpcError(#[error(cause)] jsonrpc_client_core::Error),
+}
+
+type Result<T> = std::result::Result<T, Error>;
pub struct DaemonInterface {
command_sender: Option<DaemonCommandSender>,
@@ -14,4 +33,21 @@ impl DaemonInterface {
pub fn set_command_sender(&mut self, sender: DaemonCommandSender) {
self.command_sender = Some(sender);
}
+
+ pub fn get_account_data(&self, account_token: String) -> Result<AccountData> {
+ let (tx, rx) = oneshot::channel();
+
+ self.send_command(ManagementCommand::GetAccountData(tx, account_token))?;
+
+ rx.wait()
+ .map_err(|_| Error::NoResponse)?
+ .wait()
+ .map_err(Error::RpcError)
+ }
+
+ fn send_command(&self, command: ManagementCommand) -> Result<()> {
+ let sender = self.command_sender.as_ref().ok_or(Error::NoSender)?;
+
+ sender.send(command).map_err(Error::NoDaemon)
+ }
}
diff --git a/mullvad-jni/src/from_java.rs b/mullvad-jni/src/from_java.rs
new file mode 100644
index 0000000000..526a83e1fb
--- /dev/null
+++ b/mullvad-jni/src/from_java.rs
@@ -0,0 +1,18 @@
+use jni::{objects::JString, JNIEnv};
+
+pub trait FromJava<'env> {
+ type JavaType: 'env;
+
+ fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self;
+}
+
+impl<'env> FromJava<'env> for String {
+ type JavaType = JString<'env>;
+
+ fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self {
+ String::from(
+ env.get_string(source)
+ .expect("Failed to convert from Java String"),
+ )
+ }
+}
diff --git a/mullvad-jni/src/into_java.rs b/mullvad-jni/src/into_java.rs
new file mode 100644
index 0000000000..952338ccb7
--- /dev/null
+++ b/mullvad-jni/src/into_java.rs
@@ -0,0 +1,33 @@
+use crate::get_class;
+use jni::{
+ objects::{JObject, JString, JValue},
+ JNIEnv,
+};
+use mullvad_types::account::AccountData;
+
+pub trait IntoJava<'env> {
+ type JavaType;
+
+ fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType;
+}
+
+impl<'env> IntoJava<'env> for String {
+ type JavaType = JString<'env>;
+
+ fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType {
+ env.new_string(&self).expect("Failed to create Java String")
+ }
+}
+
+impl<'env> IntoJava<'env> for AccountData {
+ type JavaType = JObject<'env>;
+
+ fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType {
+ let class = get_class("net/mullvad/mullvadvpn/model/AccountData");
+ let account_expiry = env.auto_local(JObject::from(self.expiry.to_string().into_java(env)));
+ let parameters = [JValue::Object(account_expiry.as_obj())];
+
+ env.new_object(&class, "(Ljava/lang/String;)V", &parameters)
+ .expect("Failed to create AccountData Java object")
+ }
+}
diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs
index ce6d953fcd..35d3825dcf 100644
--- a/mullvad-jni/src/lib.rs
+++ b/mullvad-jni/src/lib.rs
@@ -1,20 +1,29 @@
#![cfg(target_os = "android")]
mod daemon_interface;
+mod from_java;
+mod into_java;
-use crate::daemon_interface::DaemonInterface;
-use jni::{objects::JObject, JNIEnv};
+use crate::{daemon_interface::DaemonInterface, from_java::FromJava, into_java::IntoJava};
+use jni::{
+ objects::{GlobalRef, JObject, JString},
+ JNIEnv,
+};
use lazy_static::lazy_static;
use mullvad_daemon::{logging, version, Daemon, DaemonCommandSender, EventListener};
use mullvad_types::{relay_list::RelayList, settings::Settings};
-use parking_lot::Mutex;
-use std::{path::PathBuf, sync::mpsc, thread};
+use parking_lot::{Mutex, RwLock};
+use std::{collections::HashMap, path::PathBuf, sync::mpsc, thread};
use talpid_types::{tunnel::TunnelStateTransition, ErrorExt};
const LOG_FILENAME: &str = "daemon.log";
+const CLASSES_TO_LOAD: &[&str] = &["net/mullvad/mullvadvpn/model/AccountData"];
+
lazy_static! {
static ref DAEMON_INTERFACE: Mutex<DaemonInterface> = Mutex::new(DaemonInterface::new());
+ static ref CLASSES: RwLock<HashMap<&'static str, GlobalRef>> =
+ RwLock::new(HashMap::with_capacity(CLASSES_TO_LOAD.len()));
}
#[derive(Debug, err_derive::Error)]
@@ -28,9 +37,14 @@ pub enum Error {
#[no_mangle]
#[allow(non_snake_case)]
-pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_initialize(_: JNIEnv, _: JObject) {
+pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_initialize(
+ env: JNIEnv,
+ _: JObject,
+) {
let log_dir = start_logging();
+ load_classes(&env);
+
if let Err(error) = initialize(log_dir) {
log::error!("{}", error.display_chain());
}
@@ -47,6 +61,24 @@ fn start_logging() -> PathBuf {
log_dir
}
+fn load_classes(env: &JNIEnv) {
+ let mut classes = CLASSES.write();
+
+ for class in CLASSES_TO_LOAD {
+ classes.insert(class, load_class_reference(env, class));
+ }
+}
+
+fn load_class_reference(env: &JNIEnv, name: &str) -> GlobalRef {
+ let class = match env.find_class(name) {
+ Ok(class) => class,
+ Err(_) => panic!("Failed to find {} Java class", name),
+ };
+
+ env.new_global_ref(JObject::from(class))
+ .expect("Failed to convert local reference to Java class into a global reference")
+}
+
fn initialize(log_dir: PathBuf) -> Result<(), Error> {
let daemon_command_sender = spawn_daemon(log_dir)?;
@@ -100,3 +132,33 @@ impl EventListener for DummyListener {
fn notify_settings(&self, _: Settings) {}
fn notify_relay_list(&self, _: RelayList) {}
}
+
+fn get_class(name: &str) -> GlobalRef {
+ match CLASSES.read().get(name) {
+ Some(class) => class.clone(),
+ None => panic!("Class not loaded: {}", name),
+ }
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_getAccountData<'env, 'this>(
+ env: JNIEnv<'env>,
+ _: JObject<'this>,
+ accountToken: JString,
+) -> JObject<'env> {
+ let daemon = DAEMON_INTERFACE.lock();
+
+ let account = String::from_java(&env, accountToken);
+
+ match daemon.get_account_data(account) {
+ Ok(data) => data.into_java(&env),
+ Err(error) => {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to get account data")
+ );
+ JObject::null()
+ }
+ }
+}