summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadVpnService.kt5
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/AppVersionInfoFetcher.kt74
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt7
-rw-r--r--mullvad-jni/src/daemon_interface.rs12
-rw-r--r--mullvad-jni/src/into_java.rs24
-rw-r--r--mullvad-jni/src/lib.rs19
7 files changed, 143 insertions, 0 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
index 9de3dc12d7..751334ea61 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn
import net.mullvad.mullvadvpn.model.AccountData
+import net.mullvad.mullvadvpn.model.AppVersionInfo
import net.mullvad.mullvadvpn.model.GeoIpLocation
import net.mullvad.mullvadvpn.model.KeygenEvent
import net.mullvad.mullvadvpn.model.PublicKey
@@ -29,6 +30,7 @@ class MullvadDaemon(val vpnService: MullvadVpnService) {
external fun getRelayLocations(): RelayList
external fun getSettings(): Settings
external fun getState(): TunnelState
+ external fun getVersionInfo(): AppVersionInfo?
external fun getWireguardKey(): PublicKey?
external fun setAccount(accountToken: String?)
external fun updateRelaySettings(update: RelaySettingsUpdate)
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadVpnService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadVpnService.kt
index bc0c317cf2..6cb45329ff 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadVpnService.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadVpnService.kt
@@ -15,15 +15,19 @@ import android.net.VpnService
import android.os.Binder
import android.os.IBinder
+import net.mullvad.mullvadvpn.dataproxy.AppVersionInfoFetcher
import net.mullvad.mullvadvpn.model.TunConfig
class MullvadVpnService : VpnService() {
private val created = CompletableDeferred<Unit>()
private val binder = LocalBinder()
+ private lateinit var versionInfoFetcher: AppVersionInfoFetcher
+
val daemon = startDaemon()
override fun onCreate() {
+ versionInfoFetcher = AppVersionInfoFetcher(daemon, this)
created.complete(Unit)
}
@@ -32,6 +36,7 @@ class MullvadVpnService : VpnService() {
}
override fun onDestroy() {
+ versionInfoFetcher.stop()
daemon.cancel()
created.cancel()
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/AppVersionInfoFetcher.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/AppVersionInfoFetcher.kt
new file mode 100644
index 0000000000..2b066c934b
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/AppVersionInfoFetcher.kt
@@ -0,0 +1,74 @@
+package net.mullvad.mullvadvpn.dataproxy
+
+import java.util.Calendar
+
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+
+import android.content.Context
+
+import net.mullvad.mullvadvpn.MullvadDaemon
+
+val ONE_DAY_IN_MILLISECONDS = 24L * 60L * 60L * 1000L
+val ONE_MINUTE_IN_MILLISECONDS = 60L * 1000L
+
+val KEY_CURRENT_IS_SUPPORTED = "current_is_supported"
+val KEY_LAST_UPDATED = "last_updated"
+val KEY_LATEST_STABLE = "latest_stable"
+val KEY_LATEST = "latest"
+val SHARED_PREFERENCES = "app_version_info_cache"
+
+class AppVersionInfoFetcher(val daemon: Deferred<MullvadDaemon>, val context: Context) {
+ private val preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE)
+
+ private val mainLoop = run()
+
+ fun stop() {
+ mainLoop.cancel()
+ }
+
+ private fun run() = GlobalScope.launch(Dispatchers.Default) {
+ while (true) {
+ delay(calculateDelay())
+ fetch()
+ }
+ }
+
+ private fun calculateDelay(): Long {
+ val now = Calendar.getInstance().timeInMillis
+ val lastUpdated = preferences.getLong(KEY_LAST_UPDATED, 0)
+ val delta = now - lastUpdated
+
+ if (delta < 0 || delta >= ONE_DAY_IN_MILLISECONDS) {
+ return 0
+ } else {
+ return ONE_DAY_IN_MILLISECONDS - delta
+ }
+ }
+
+ private suspend fun fetch() {
+ var now = Calendar.getInstance().timeInMillis
+ var versionInfo = daemon.await().getVersionInfo()
+ var attempt = 0
+
+ while (attempt < 5 && versionInfo == null) {
+ delay(ONE_MINUTE_IN_MILLISECONDS)
+ now = Calendar.getInstance().timeInMillis
+ versionInfo = daemon.await().getVersionInfo()
+ attempt += 1
+ }
+
+ if (versionInfo != null) {
+ preferences.edit().apply {
+ putLong(KEY_LAST_UPDATED, now)
+ putBoolean(KEY_CURRENT_IS_SUPPORTED, versionInfo.currentIsSupported)
+ putString(KEY_LATEST_STABLE, versionInfo.latestStable)
+ putString(KEY_LATEST, versionInfo.latest)
+ commit()
+ }
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt
new file mode 100644
index 0000000000..0c41090f3f
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt
@@ -0,0 +1,7 @@
+package net.mullvad.mullvadvpn.model
+
+data class AppVersionInfo(
+ val currentIsSupported: Boolean,
+ val latestStable: String,
+ val latest: String
+)
diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs
index 9cada243af..5ba890f90d 100644
--- a/mullvad-jni/src/daemon_interface.rs
+++ b/mullvad-jni/src/daemon_interface.rs
@@ -7,6 +7,7 @@ use mullvad_types::{
relay_list::RelayList,
settings::Settings,
states::{TargetState, TunnelState},
+ version::AppVersionInfo,
wireguard::KeygenEvent,
};
use parking_lot::Mutex;
@@ -128,6 +129,17 @@ impl DaemonInterface {
Ok(rx.wait().map_err(|_| Error::NoResponse)?)
}
+ pub fn get_version_info(&self) -> Result<AppVersionInfo> {
+ let (tx, rx) = oneshot::channel();
+
+ self.send_command(ManagementCommand::GetVersionInfo(tx))?;
+
+ rx.wait()
+ .map_err(|_| Error::NoResponse)?
+ .wait()
+ .map_err(Error::RpcError)
+ }
+
pub fn get_wireguard_key(&self) -> Result<Option<wireguard::PublicKey>> {
let (tx, rx) = oneshot::channel();
diff --git a/mullvad-jni/src/into_java.rs b/mullvad-jni/src/into_java.rs
index fd5dbc8427..52aa9e2f7c 100644
--- a/mullvad-jni/src/into_java.rs
+++ b/mullvad-jni/src/into_java.rs
@@ -13,6 +13,7 @@ use mullvad_types::{
relay_list::{Relay, RelayList, RelayListCity, RelayListCountry},
settings::Settings,
states::TunnelState,
+ version::AppVersionInfo,
wireguard::KeygenEvent,
CustomTunnelEndpoint,
};
@@ -187,6 +188,29 @@ impl<'env> IntoJava<'env> for PublicKey {
}
}
+impl<'env> IntoJava<'env> for AppVersionInfo {
+ type JavaType = JObject<'env>;
+
+ fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType {
+ let class = get_class("net/mullvad/mullvadvpn/model/AppVersionInfo");
+ let current_is_supported = self.current_is_supported as jboolean;
+ let latest_stable = env.auto_local(*self.latest_stable.into_java(env));
+ let latest = env.auto_local(*self.latest.into_java(env));
+ let parameters = [
+ JValue::Bool(current_is_supported),
+ JValue::Object(latest_stable.as_obj()),
+ JValue::Object(latest.as_obj()),
+ ];
+
+ env.new_object(
+ &class,
+ "(ZLjava/lang/String;Ljava/lang/String;)V",
+ &parameters,
+ )
+ .expect("Failed to create AppVersionInfo Java object")
+ }
+}
+
impl<'env> IntoJava<'env> for AccountData {
type JavaType = JObject<'env>;
diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs
index 7b56831ee7..31d9cdf670 100644
--- a/mullvad-jni/src/lib.rs
+++ b/mullvad-jni/src/lib.rs
@@ -37,6 +37,7 @@ const CLASSES_TO_LOAD: &[&str] = &[
"net/mullvad/mullvadvpn/model/ActionAfterDisconnect$Block",
"net/mullvad/mullvadvpn/model/ActionAfterDisconnect$Nothing",
"net/mullvad/mullvadvpn/model/ActionAfterDisconnect$Reconnect",
+ "net/mullvad/mullvadvpn/model/AppVersionInfo",
"net/mullvad/mullvadvpn/model/Constraint$Any",
"net/mullvad/mullvadvpn/model/Constraint$Only",
"net/mullvad/mullvadvpn/model/GeoIpLocation",
@@ -366,6 +367,24 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_getState<'env,
#[no_mangle]
#[allow(non_snake_case)]
+pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_getVersionInfo<'env, 'this>(
+ env: JNIEnv<'env>,
+ _: JObject<'this>,
+) -> JObject<'env> {
+ match DAEMON_INTERFACE.get_version_info() {
+ Ok(version_info) => version_info.into_java(&env),
+ Err(error) => {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to get version information")
+ );
+ JObject::null()
+ }
+ }
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_getWireguardKey<'env, 'this>(
env: JNIEnv<'env>,
_: JObject<'this>,