diff options
| author | Jonathan <jonathan@mullvad.net> | 2023-10-03 18:06:55 +0200 |
|---|---|---|
| committer | Jonathan <jonathan@mullvad.net> | 2023-10-16 18:34:07 +0200 |
| commit | 9204e1c66e710d22ebf7b55ab6ec045cc53bdad9 (patch) | |
| tree | 83745d24e9e09a81f6b9cb7884f04ba05a39e371 | |
| parent | d32b6f81ceb8c383588678e8fbabe613fbac4142 (diff) | |
| download | mullvadvpn-9204e1c66e710d22ebf7b55ab6ec045cc53bdad9.tar.xz mullvadvpn-9204e1c66e710d22ebf7b55ab6ec045cc53bdad9.zip | |
Add piping for google play payment API requests
This commit adds all of the basic piping in order to let Android use the
JNI interface in order to make requests to our API pertaining to google
play payment initialization and status.
| -rw-r--r-- | mullvad-api/src/lib.rs | 59 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/api.rs | 55 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/mod.rs | 123 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/service.rs | 41 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 52 | ||||
| -rw-r--r-- | mullvad-jni/src/daemon_interface.rs | 22 | ||||
| -rw-r--r-- | mullvad-jni/src/lib.rs | 114 | ||||
| -rw-r--r-- | mullvad-types/src/account.rs | 17 |
8 files changed, 475 insertions, 8 deletions
diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs index 37faf5c40c..647b5cf071 100644 --- a/mullvad-api/src/lib.rs +++ b/mullvad-api/src/lib.rs @@ -6,7 +6,7 @@ use futures::channel::mpsc; use futures::Stream; use hyper::Method; use mullvad_types::{ - account::{AccountToken, VoucherSubmission}, + account::{AccountToken, PlayPurchase, PlayPurchasePaymentToken, VoucherSubmission}, version::AppVersion, }; use proxy::ApiConnectionMode; @@ -63,6 +63,7 @@ pub const API_IP_CACHE_FILENAME: &str = "api-ip-address.txt"; const ACCOUNTS_URL_PREFIX: &str = "accounts/v1"; const APP_URL_PREFIX: &str = "app/v1"; +const GOOGLE_PAYMENTS_URL_PREFIX: &str = "payments/google-play/v1"; pub static API: LazyManual<ApiEndpoint> = LazyManual::new(ApiEndpoint::from_env_vars); @@ -457,6 +458,62 @@ impl AccountsProxy { } } + pub fn init_play_purchase( + &mut self, + account_token: AccountToken, + ) -> impl Future<Output = Result<PlayPurchasePaymentToken, rest::Error>> { + #[derive(serde::Deserialize)] + struct PlayPurchaseInitResponse { + obfuscated_id: String, + } + + let service = self.handle.service.clone(); + let factory = self.handle.factory.clone(); + let access_proxy = self.handle.token_store.clone(); + + async move { + let response = rest::send_json_request( + &factory, + service, + &format!("{GOOGLE_PAYMENTS_URL_PREFIX}/init"), + Method::POST, + &(), + Some((access_proxy, account_token)), + &[StatusCode::OK], + ) + .await; + + let PlayPurchaseInitResponse { obfuscated_id } = + rest::deserialize_body(response?).await?; + + Ok(obfuscated_id) + } + } + + pub fn verify_play_purchase( + &mut self, + account_token: AccountToken, + play_purchase: PlayPurchase, + ) -> impl Future<Output = Result<(), rest::Error>> { + let service = self.handle.service.clone(); + let factory = self.handle.factory.clone(); + let access_proxy = self.handle.token_store.clone(); + + async move { + rest::send_json_request( + &factory, + service, + &format!("{GOOGLE_PAYMENTS_URL_PREFIX}/acknowledge"), + Method::POST, + &play_purchase, + Some((access_proxy, account_token)), + &[StatusCode::ACCEPTED], + ) + .await?; + Ok(()) + } + } + pub fn get_www_auth_token( &self, account: AccountToken, diff --git a/mullvad-daemon/src/device/api.rs b/mullvad-daemon/src/device/api.rs index 93dae78261..07e7b35311 100644 --- a/mullvad-daemon/src/device/api.rs +++ b/mullvad-daemon/src/device/api.rs @@ -2,7 +2,11 @@ use std::pin::Pin; use chrono::{DateTime, Utc}; use futures::{future::FusedFuture, Future}; -use mullvad_types::{account::VoucherSubmission, device::Device, wireguard::WireguardData}; +use mullvad_types::{ + account::{PlayPurchasePaymentToken, VoucherSubmission}, + device::Device, + wireguard::WireguardData, +}; use super::{Error, PrivateAccountAndDevice, ResponseTx}; @@ -47,6 +51,25 @@ impl CurrentApiCall { self.current_call = Some(Call::VoucherSubmission(voucher_call, Some(tx))); } + pub fn set_init_play_purchase( + &mut self, + init_play_purchase_call: ApiCall<PlayPurchasePaymentToken>, + tx: ResponseTx<PlayPurchasePaymentToken>, + ) { + self.current_call = Some(Call::InitPlayPurchase(init_play_purchase_call, Some(tx))); + } + + pub fn set_verify_play_purchase( + &mut self, + verify_play_purchase_call: ApiCall<()>, + tx: ResponseTx<()>, + ) { + self.current_call = Some(Call::VerifyPlayPurchase( + verify_play_purchase_call, + Some(tx), + )); + } + pub fn is_validating(&self) -> bool { matches!( &self.current_call, @@ -109,6 +132,11 @@ enum Call { ApiCall<VoucherSubmission>, Option<ResponseTx<VoucherSubmission>>, ), + InitPlayPurchase( + ApiCall<PlayPurchasePaymentToken>, + Option<ResponseTx<PlayPurchasePaymentToken>>, + ), + VerifyPlayPurchase(ApiCall<()>, Option<ResponseTx<()>>), ExpiryCheck(ApiCall<DateTime<Utc>>), } @@ -142,6 +170,26 @@ impl futures::Future for Call { std::task::Poll::Pending } } + InitPlayPurchase(call, tx) => { + if let std::task::Poll::Ready(response) = Pin::new(call).poll(cx) { + std::task::Poll::Ready(ApiResult::InitPlayPurchase( + response, + tx.take().unwrap(), + )) + } else { + std::task::Poll::Pending + } + } + VerifyPlayPurchase(call, tx) => { + if let std::task::Poll::Ready(response) = Pin::new(call).poll(cx) { + std::task::Poll::Ready(ApiResult::VerifyPlayPurchase( + response, + tx.take().unwrap(), + )) + } else { + std::task::Poll::Pending + } + } ExpiryCheck(call) => Pin::new(call).poll(cx).map(ApiResult::ExpiryCheck), } } @@ -155,5 +203,10 @@ pub(crate) enum ApiResult { Result<VoucherSubmission, Error>, ResponseTx<VoucherSubmission>, ), + InitPlayPurchase( + Result<PlayPurchasePaymentToken, Error>, + ResponseTx<PlayPurchasePaymentToken>, + ), + VerifyPlayPurchase(Result<(), Error>, ResponseTx<()>), ExpiryCheck(Result<DateTime<Utc>, Error>), } diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs index b54638f140..528c25ce9a 100644 --- a/mullvad-daemon/src/device/mod.rs +++ b/mullvad-daemon/src/device/mod.rs @@ -6,7 +6,7 @@ use futures::{ use mullvad_api::rest; use mullvad_types::{ - account::{AccountToken, VoucherSubmission}, + account::{AccountToken, PlayPurchase, PlayPurchasePaymentToken, VoucherSubmission}, device::{ AccountAndDevice, Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceName, DeviceState, }, @@ -305,6 +305,8 @@ enum AccountManagerCommand { SetRotationInterval(RotationInterval, ResponseTx<()>), ValidateDevice(ResponseTx<()>), SubmitVoucher(String, ResponseTx<VoucherSubmission>), + InitPlayPurchase(ResponseTx<PlayPurchasePaymentToken>), + VerifyPlayPurchase(ResponseTx<()>, PlayPurchase), CheckExpiry(ResponseTx<DateTime<Utc>>), Shutdown(oneshot::Sender<()>), } @@ -359,6 +361,16 @@ impl AccountManagerHandle { .await } + pub async fn init_play_purchase(&self) -> Result<PlayPurchasePaymentToken, Error> { + self.send_command(AccountManagerCommand::InitPlayPurchase) + .await + } + + pub async fn verify_play_purchase(&self, play_purchase: PlayPurchase) -> Result<(), Error> { + self.send_command(move |tx| AccountManagerCommand::VerifyPlayPurchase(tx, play_purchase)) + .await + } + pub async fn check_expiry(&self) -> Result<DateTime<Utc>, Error> { self.send_command(AccountManagerCommand::CheckExpiry).await } @@ -514,6 +526,12 @@ impl AccountManager { Some(AccountManagerCommand::SubmitVoucher(voucher, tx)) => { self.handle_voucher_submission(tx, voucher, &mut current_api_call); }, + Some(AccountManagerCommand::InitPlayPurchase(tx)) => { + self.handle_init_play_purchase(tx, &mut current_api_call); + }, + Some(AccountManagerCommand::VerifyPlayPurchase(tx, play_purchase)) => { + self.handle_verify_play_purchase(tx, play_purchase, &mut current_api_call); + }, Some(AccountManagerCommand::CheckExpiry(tx)) => { self.handle_expiry_request(tx, &mut current_api_call); }, @@ -589,6 +607,65 @@ impl AccountManager { } } + fn handle_init_play_purchase( + &mut self, + tx: ResponseTx<PlayPurchasePaymentToken>, + current_api_call: &mut api::CurrentApiCall, + ) { + if current_api_call.is_logging_in() { + let _ = tx.send(Err(Error::AccountChange)); + return; + } + + let init_play_purchase_api_call = move || { + let old_config = self.data.device().ok_or(Error::NoDevice)?; + let account_token = old_config.account_token.clone(); + let account_service = self.account_service.clone(); + Ok(async move { account_service.init_play_purchase(account_token).await }) + }; + + match init_play_purchase_api_call() { + Ok(call) => { + current_api_call.set_init_play_purchase(Box::pin(call), tx); + } + Err(err) => { + let _ = tx.send(Err(err)); + } + } + } + + fn handle_verify_play_purchase( + &mut self, + tx: ResponseTx<()>, + play_purchase: PlayPurchase, + current_api_call: &mut api::CurrentApiCall, + ) { + if current_api_call.is_logging_in() { + let _ = tx.send(Err(Error::AccountChange)); + return; + } + + let play_purchase_verify_api_call = move || { + let old_config = self.data.device().ok_or(Error::NoDevice)?; + let account_token = old_config.account_token.clone(); + let account_service = self.account_service.clone(); + Ok(async move { + account_service + .verify_play_purchase(account_token, play_purchase) + .await + }) + }; + + match play_purchase_verify_api_call() { + Ok(call) => { + current_api_call.set_verify_play_purchase(Box::pin(call), tx); + } + Err(err) => { + let _ = tx.send(Err(err)); + } + } + } + fn handle_expiry_request( &mut self, tx: ResponseTx<DateTime<Utc>>, @@ -627,6 +704,14 @@ impl AccountManager { VoucherSubmission(data_response, tx) => { self.consume_voucher_result(data_response, tx).await } + InitPlayPurchase(data_response, tx) => { + self.consume_init_play_purchase_result(data_response, tx) + .await + } + VerifyPlayPurchase(data_response, tx) => { + self.consume_verify_play_purchase_result(data_response, tx) + .await + } ExpiryCheck(data_response) => self.consume_expiry_result(data_response).await, } } @@ -665,6 +750,42 @@ impl AccountManager { let _ = tx.send(response); } + async fn consume_init_play_purchase_result( + &mut self, + response: Result<PlayPurchasePaymentToken, Error>, + tx: ResponseTx<PlayPurchasePaymentToken>, + ) { + match &response { + Ok(_) => (), + Err(Error::InvalidAccount) => { + self.revoke_device(|| Error::InvalidAccount).await; + } + Err(Error::InvalidDevice) => { + self.revoke_device(|| Error::InvalidDevice).await; + } + Err(err) => log::error!("Failed to initialize play purchase: {}", err), + } + let _ = tx.send(response); + } + + async fn consume_verify_play_purchase_result( + &mut self, + response: Result<(), Error>, + tx: ResponseTx<()>, + ) { + match &response { + Ok(_) => (), + Err(Error::InvalidAccount) => { + self.revoke_device(|| Error::InvalidAccount).await; + } + Err(Error::InvalidDevice) => { + self.revoke_device(|| Error::InvalidDevice).await; + } + Err(err) => log::error!("Failed to verify play purchase: {}", err), + } + let _ = tx.send(response); + } + async fn consume_expiry_result(&mut self, response: Result<DateTime<Utc>, Error>) { match response { Ok(expiry) => { diff --git a/mullvad-daemon/src/device/service.rs b/mullvad-daemon/src/device/service.rs index 9c967e4413..85d9d56d84 100644 --- a/mullvad-daemon/src/device/service.rs +++ b/mullvad-daemon/src/device/service.rs @@ -3,7 +3,7 @@ use std::{future::Future, time::Duration}; use chrono::{DateTime, Utc}; use futures::future::{abortable, AbortHandle}; use mullvad_types::{ - account::{AccountToken, VoucherSubmission}, + account::{AccountToken, PlayPurchase, PlayPurchasePaymentToken, VoucherSubmission}, device::{Device, DeviceId}, wireguard::WireguardData, }; @@ -320,6 +320,45 @@ impl AccountService { } result.map_err(map_rest_error) } + + pub async fn init_play_purchase( + &self, + account_token: AccountToken, + ) -> Result<PlayPurchasePaymentToken, Error> { + let mut proxy = self.proxy.clone(); + let api_handle = self.api_availability.clone(); + let result = retry_future( + move || proxy.init_play_purchase(account_token.clone()), + move |result| should_retry(result, &api_handle), + RETRY_ACTION_STRATEGY, + ) + .await; + if result.is_ok() { + self.initial_check_abort_handle.abort(); + self.api_availability.resume_background(); + } + result.map_err(map_rest_error) + } + + pub async fn verify_play_purchase( + &self, + account_token: AccountToken, + play_purchase: PlayPurchase, + ) -> Result<(), Error> { + let mut proxy = self.proxy.clone(); + let api_handle = self.api_availability.clone(); + let result = retry_future( + move || proxy.verify_play_purchase(account_token.clone(), play_purchase.clone()), + move |result| should_retry(result, &api_handle), + RETRY_ACTION_STRATEGY, + ) + .await; + if result.is_ok() { + self.initial_check_abort_handle.abort(); + self.api_availability.resume_background(); + } + result.map_err(map_rest_error) + } } pub fn spawn_account_service( diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index f7343acc87..ceb50ca2b2 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -40,7 +40,7 @@ use mullvad_relay_selector::{ }; use mullvad_types::{ access_method::{AccessMethod, AccessMethodSetting}, - account::{AccountData, AccountToken, VoucherSubmission}, + account::{AccountData, AccountToken, PlayPurchase, PlayPurchasePaymentToken, VoucherSubmission}, auth_failed::AuthFailed, custom_list::CustomList, device::{Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceState, RemoveDeviceEvent}, @@ -178,6 +178,16 @@ pub enum Error { #[cfg(target_os = "macos")] #[error(display = "Failed to set exclusion group")] GroupIdError(#[error(source)] io::Error), + + // TODO + //#[cfg(target_os = "android")] + #[error(display = "Failed to initialize play purchase")] + InitPlayPurchase(#[error(source)] device::Error), + + // TODO + //#[cfg(target_os = "android")] + #[error(display = "Failed to verify play purchase")] + VerifyPlayPurchase(#[error(source)] device::Error), } /// Enum representing commands that can be sent to the daemon. @@ -327,6 +337,14 @@ pub enum DaemonCommand { /// to bypass the tunnel in blocking states. #[cfg(target_os = "android")] BypassSocket(RawFd, oneshot::Sender<()>), + /// Initialize a google play purchase through the API. + ///TODO + //#[cfg(target_os = "android")] + InitPlayPurchase(ResponseTx<PlayPurchasePaymentToken, Error>), + /// Verify that a google play payment was successful through the API. + ///TODO + //#[cfg(target_os = "android")] + VerifyPlayPurchase(ResponseTx<(), Error>, PlayPurchase), } /// All events that can happen in the daemon. Sent from various threads and exposed interfaces. @@ -1027,6 +1045,10 @@ where GetAccountData(tx, account_token) => self.on_get_account_data(tx, account_token), GetWwwAuthToken(tx) => self.on_get_www_auth_token(tx).await, SubmitVoucher(tx, voucher) => self.on_submit_voucher(tx, voucher), + InitPlayPurchase(tx) => self.on_init_play_purchase(tx), + VerifyPlayPurchase(tx, play_purchase) => { + self.on_verify_play_purchase(tx, play_purchase) + } GetRelayLocations(tx) => self.on_get_relay_locations(tx), UpdateRelayLocations => self.on_update_relay_locations().await, LoginAccount(tx, account_token) => self.on_login_account(tx, account_token), @@ -1404,6 +1426,34 @@ where }); } + fn on_init_play_purchase(&mut self, tx: ResponseTx<PlayPurchasePaymentToken, Error>) { + let manager = self.account_manager.clone(); + tokio::spawn(async move { + Self::oneshot_send( + tx, + manager + .init_play_purchase() + .await + .map_err(Error::InitPlayPurchase), + "init_play_purchase response", + ); + }); + } + + fn on_verify_play_purchase(&mut self, tx: ResponseTx<(), Error>, play_purchase: PlayPurchase) { + let manager = self.account_manager.clone(); + tokio::spawn(async move { + Self::oneshot_send( + tx, + manager + .verify_play_purchase(play_purchase) + .await + .map_err(Error::VerifyPlayPurchase), + "verify_play_purchase response", + ); + }); + } + fn on_get_relay_locations(&mut self, tx: oneshot::Sender<RelayList>) { Self::oneshot_send(tx, self.relay_selector.get_locations(), "relay locations"); } diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs index 771c432a7a..c64c94041e 100644 --- a/mullvad-jni/src/daemon_interface.rs +++ b/mullvad-jni/src/daemon_interface.rs @@ -1,7 +1,7 @@ use futures::{channel::oneshot, executor::block_on}; use mullvad_daemon::{device, DaemonCommand, DaemonCommandSender}; use mullvad_types::{ - account::{AccountData, AccountToken, VoucherSubmission}, + account::{AccountData, AccountToken, PlayPurchase, VoucherSubmission}, device::{Device, DeviceState}, location::GeoIpLocation, relay_constraints::{ObfuscationSettings, RelaySettingsUpdate}, @@ -307,6 +307,26 @@ impl DaemonInterface { .map_err(Error::from) } + pub fn init_play_purchase(&self) -> Result<String> { + let (tx, rx) = oneshot::channel(); + + self.send_command(DaemonCommand::InitPlayPurchase(tx))?; + + block_on(rx) + .map_err(|_| Error::NoResponse)? + .map_err(Error::from) + } + + pub fn verify_play_purchase(&self, play_purchase: PlayPurchase) -> Result<()> { + let (tx, rx) = oneshot::channel(); + + self.send_command(DaemonCommand::VerifyPlayPurchase(tx, play_purchase))?; + + block_on(rx) + .map_err(|_| Error::NoResponse)? + .map_err(Error::from) + } + pub fn update_relay_settings(&self, update: RelaySettingsUpdate) -> Result<()> { let (tx, rx) = oneshot::channel(); diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs index 6f33938e99..ff7a978799 100644 --- a/mullvad-jni/src/lib.rs +++ b/mullvad-jni/src/lib.rs @@ -24,7 +24,7 @@ use mullvad_daemon::{ DaemonCommandChannel, }; use mullvad_types::{ - account::{AccountData, VoucherSubmission}, + account::{AccountData, PlayPurchase, VoucherSubmission}, settings::DnsOptions, }; use std::{ @@ -191,6 +191,62 @@ impl From<daemon_interface::Error> for VoucherSubmissionError { } } +#[derive(IntoJava)] +#[jnix(package = "net.mullvad.mullvadvpn.model")] +pub enum PlayPurchaseInitResult { + Ok(String), + Error(PlayPurchaseInitError), +} + +#[derive(IntoJava)] +#[jnix(package = "net.mullvad.mullvadvpn.model")] +pub enum PlayPurchaseInitError { + OtherError, +} + +impl From<Result<String, daemon_interface::Error>> for PlayPurchaseInitResult { + fn from(result: Result<String, daemon_interface::Error>) -> Self { + match result { + Ok(obfuscated_id) => PlayPurchaseInitResult::Ok(obfuscated_id), + Err(error) => PlayPurchaseInitResult::Error(error.into()), + } + } +} + +impl From<daemon_interface::Error> for PlayPurchaseInitError { + fn from(error: daemon_interface::Error) -> Self { + PlayPurchaseInitError::OtherError + } +} + +#[derive(IntoJava)] +#[jnix(package = "net.mullvad.mullvadvpn.model")] +pub enum PlayPurchaseVerifyResult { + Ok, + Error(PlayPurchaseVerifyError), +} + +#[derive(IntoJava)] +#[jnix(package = "net.mullvad.mullvadvpn.model")] +pub enum PlayPurchaseVerifyError { + OtherError, +} + +impl From<Result<(), daemon_interface::Error>> for PlayPurchaseVerifyResult { + fn from(result: Result<(), daemon_interface::Error>) -> Self { + match result { + Ok(()) => PlayPurchaseVerifyResult::Ok, + Err(error) => PlayPurchaseVerifyResult::Error(error.into()), + } + } +} + +impl From<daemon_interface::Error> for PlayPurchaseVerifyError { + fn from(error: daemon_interface::Error) -> Self { + PlayPurchaseVerifyError::OtherError + } +} + #[no_mangle] #[allow(non_snake_case)] pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_initialize( @@ -1194,6 +1250,62 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_submitV #[no_mangle] #[allow(non_snake_case)] +pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_initPlayPurchase<'env>( + env: JNIEnv<'env>, + _: JObject<'_>, + daemon_interface_address: jlong, +) -> JObject<'env> { + let env = JnixEnv::from(env); + + let result = + // SAFETY: The address points to an instance valid for the duration of this function call + if let Some(daemon_interface) = unsafe { get_daemon_interface(daemon_interface_address) } { + let raw_result = daemon_interface.init_play_purchase(); + + if let Err(ref error) = &raw_result { + log_request_error("init google play purchase", error); + } + + PlayPurchaseInitResult::from(raw_result) + } else { + PlayPurchaseInitResult::Error(PlayPurchaseInitError::OtherError) + }; + + result.into_java(&env).forget() +} + +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_verifyPlayPurchase< + 'env, +>( + env: JNIEnv<'env>, + _: JObject<'_>, + daemon_interface_address: jlong, + play_purchase: JObject<'_>, +) -> JObject<'env> { + let env = JnixEnv::from(env); + + let result = + // SAFETY: The address points to an instance valid for the duration of this function call + if let Some(daemon_interface) = unsafe { get_daemon_interface(daemon_interface_address) } { + let play_purchase = PlayPurchase::from_java(&env, play_purchase); + let raw_result = daemon_interface.verify_play_purchase(play_purchase); + + if let Err(ref error) = &raw_result { + log_request_error("verify google play purchase", error); + } + + PlayPurchaseVerifyResult::from(raw_result) + } else { + PlayPurchaseVerifyResult::Error(PlayPurchaseVerifyError::OtherError) + }; + + result.into_java(&env).forget() +} + +#[no_mangle] +#[allow(non_snake_case)] pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_updateRelaySettings( env: JNIEnv<'_>, _: JObject<'_>, diff --git a/mullvad-types/src/account.rs b/mullvad-types/src/account.rs index 16f6a963f2..2d12a80a2a 100644 --- a/mullvad-types/src/account.rs +++ b/mullvad-types/src/account.rs @@ -1,6 +1,6 @@ use chrono::{offset::Utc, DateTime}; #[cfg(target_os = "android")] -use jnix::IntoJava; +use jnix::{FromJava, IntoJava}; use serde::{Deserialize, Serialize}; /// Identifier used to identify a Mullvad account. @@ -9,6 +9,11 @@ pub type AccountToken = String; /// Identifier used to authenticate a Mullvad account. pub type AccessToken = String; +// TODO: Should be only android +/// The payment token returned by initiating a google play purchase. +/// In the API this is called the `obfuscated_id`. +pub type PlayPurchasePaymentToken = String; + /// Account expiration info returned by the API via `/v1/me`. #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[cfg_attr(target_os = "android", derive(IntoJava))] @@ -39,6 +44,16 @@ pub struct VoucherSubmission { pub new_expiry: DateTime<Utc>, } +/// TODO Should be only android +/// `PlayPurchase` is provided to google in order to verify that a google play purchase was acknowledged. +#[derive(Deserialize, Serialize, Debug, Clone)] +#[cfg_attr(target_os = "android", derive(FromJava))] +#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))] +pub struct PlayPurchase { + pub product_id: String, + pub purchase_token: String, +} + /// Token used for authentication in the API. #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct AccessTokenData { |
