From be342cf162ef41f05307dc6d2f29755534381bcd Mon Sep 17 00:00:00 2001 From: saber safavi Date: Tue, 5 Dec 2023 15:29:29 +0100 Subject: [PATCH] Add boot receiver to start daemon service --- .../compose/screen/VpnSettingsScreenTest.kt | 1 + android/app/src/main/AndroidManifest.xml | 9 ++++ .../compose/screen/VpnSettingsScreen.kt | 13 +++++ .../compose/state/VpnSettingsUiState.kt | 3 ++ .../compose/util/BootCompletedReceiver.kt | 48 +++++++++++++++++++ .../net/mullvad/mullvadvpn/di/UiModule.kt | 8 +++- .../repository/SettingsRepository.kt | 24 +++++++++- .../ui/fragment/VpnSettingsFragment.kt | 1 + .../viewmodel/VpnSettingsViewModel.kt | 15 ++++-- .../viewmodel/VpnSettingsViewModelState.kt | 3 ++ .../viewmodel/VpnSettingsViewModelTest.kt | 1 + .../resource/src/main/res/values/strings.xml | 1 + gui/locales/messages.pot | 3 ++ 13 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/BootCompletedReceiver.kt diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt index 9b6dd9e492ba..f1cc31e04857 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt @@ -623,6 +623,7 @@ class VpnSettingsScreenTest { uiState = VpnSettingsUiState.createDefault( quantumResistant = QuantumResistantState.Auto, + isConnectOnBootEnabled = false ), onSelectQuantumResistanceSetting = mockSelectQuantumResistantSettingListener, toastMessagesSharedFlow = MutableSharedFlow().asSharedFlow() diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index feba1dabf859..7f017462e977 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + + + + + + + diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt index 7290b9600fd3..53a760c94011 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt @@ -99,6 +99,7 @@ private fun PreviewVpnSettings() { onRestoreMtuClick = {}, onCancelMtuDialogClick = {}, onToggleAutoConnect = {}, + onToggleConnectOnBoot = {}, onToggleLocalNetworkSharing = {}, onToggleDnsClick = {}, onToggleBlockAds = {}, @@ -143,6 +144,7 @@ fun VpnSettingsScreen( onRestoreMtuClick: () -> Unit = {}, onCancelMtuDialogClick: () -> Unit = {}, onToggleAutoConnect: (Boolean) -> Unit = {}, + onToggleConnectOnBoot: (Boolean) -> Unit = {}, onToggleLocalNetworkSharing: (Boolean) -> Unit = {}, onToggleDnsClick: (Boolean) -> Unit = {}, onToggleBlockAds: (Boolean) -> Unit = {}, @@ -281,6 +283,17 @@ fun VpnSettingsScreen( onCellClicked = { newValue -> onToggleAutoConnect(newValue) } ) } + if (uiState.isConnectOnBootEnabled != null) { + item { + Divider() + HeaderSwitchComposeCell( + title = stringResource(R.string.connect_on_boot), + isToggled = uiState.isConnectOnBootEnabled, + isEnabled = true, + onCellClicked = { newValue -> onToggleConnectOnBoot(newValue) } + ) + } + } item { SwitchComposeSubtitleCell(text = stringResource(id = R.string.auto_connect_footer)) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt index e78d2e9f436f..01ad206837d6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt @@ -12,6 +12,7 @@ import net.mullvad.mullvadvpn.viewmodel.StagedDns data class VpnSettingsUiState( val mtu: String, val isAutoConnectEnabled: Boolean, + val isConnectOnBootEnabled: Boolean?, val isLocalNetworkSharingEnabled: Boolean, val isCustomDnsEnabled: Boolean, val customDnsItems: List, @@ -28,6 +29,7 @@ data class VpnSettingsUiState( fun createDefault( mtu: String = "", isAutoConnectEnabled: Boolean = false, + isConnectOnBootEnabled: Boolean? = null, isLocalNetworkSharingEnabled: Boolean = false, isCustomDnsEnabled: Boolean = false, customDnsItems: List = emptyList(), @@ -42,6 +44,7 @@ data class VpnSettingsUiState( VpnSettingsUiState( mtu, isAutoConnectEnabled, + isConnectOnBootEnabled, isLocalNetworkSharingEnabled, isCustomDnsEnabled, customDnsItems, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/BootCompletedReceiver.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/BootCompletedReceiver.kt new file mode 100644 index 000000000000..d74c3d40c333 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/BootCompletedReceiver.kt @@ -0,0 +1,48 @@ +package net.mullvad.mullvadvpn.compose.util + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log +import net.mullvad.mullvadvpn.di.APP_PREFERENCES_NAME +import net.mullvad.mullvadvpn.lib.common.constant.KEY_CONNECT_ACTION +import net.mullvad.mullvadvpn.lib.common.constant.VPN_SERVICE_CLASS +import net.mullvad.mullvadvpn.repository.IS_CONNECT_ON_BOOT_ENABLED_KEY + +private const val TAG = "AAAAAAAAAAAABootBroadCast" + +class BootCompletedReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context?, mBootIntent: Intent?) { + Log.d(TAG, "AAAAA @Boot ") + if ("android.intent.action.BOOT_COMPLETED" == mBootIntent?.action) { + Log.d(TAG, "AAAAA @Boot actionCaught :" + mBootIntent.action) + // Now you are getting your Boot receiver + context?.let { + Log.d(TAG, "AAAAA @Boot context :" + mBootIntent.action) + if (isConnectOnBootEnabled(it)) { + Log.d(TAG, "AAAAA @Boot Service :" + mBootIntent.action) + startDaemonService(it) + } else { + + Log.d(TAG, "AAAAA @Boot isConnectOnBootEnabled is false:") + } + } + } + } + + private fun isConnectOnBootEnabled(context: Context): Boolean { + return context + .getSharedPreferences(APP_PREFERENCES_NAME, Context.MODE_PRIVATE) + .getBoolean(IS_CONNECT_ON_BOOT_ENABLED_KEY, false) + } + + private fun startDaemonService(context: Context) { + val intent = + Intent().apply { + setClassName(context.packageName, VPN_SERVICE_CLASS) + action = KEY_CONNECT_ACTION + } + context.startForegroundService(intent) + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index 9e35e67823a6..f77d26f25020 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -92,7 +92,13 @@ val uiModule = module { androidContext().getSharedPreferences(APP_PREFERENCES_NAME, Context.MODE_PRIVATE) ) } - single { SettingsRepository(get()) } + single { + SettingsRepository( + get(), + androidContext().getSharedPreferences(APP_PREFERENCES_NAME, Context.MODE_PRIVATE), + get() + ) + } single { MullvadProblemReport(get()) } single { AccountExpiryNotificationUseCase(get()) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt index ac9637c68342..cc64e0a57697 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt @@ -1,9 +1,12 @@ package net.mullvad.mullvadvpn.repository +import android.content.SharedPreferences +import android.content.pm.PackageManager import java.net.InetAddress import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf @@ -22,10 +25,22 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.settingsListener import net.mullvad.mullvadvpn.util.callbackFlowFromNotifier import net.mullvad.mullvadvpn.util.flatMapReadyConnectionOrDefault +internal const val IS_CONNECT_ON_BOOT_ENABLED_KEY = "is_connect_on_boot_enabled" + class SettingsRepository( private val serviceConnectionManager: ServiceConnectionManager, + private val sharedPreferences: SharedPreferences, + packageManager: PackageManager, dispatcher: CoroutineDispatcher = Dispatchers.IO ) { + + var isConnectOnBootEnabled: MutableStateFlow = + MutableStateFlow( + if (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) + sharedPreferences.getBoolean(IS_CONNECT_ON_BOOT_ENABLED_KEY, false) + else null + ) + val settingsUpdates: StateFlow = serviceConnectionManager.connectionState .flatMapReadyConnectionOrDefault(flowOf()) { state -> @@ -46,8 +61,8 @@ class SettingsRepository( DnsOptions( state = if (isCustomDnsEnabled) DnsState.Custom else DnsState.Default, customOptions = CustomDnsOptions(ArrayList(dnsList)), - defaultOptions = contentBlockersOptions - ) + defaultOptions = contentBlockersOptions, + ), ) } @@ -67,6 +82,11 @@ class SettingsRepository( serviceConnectionManager.settingsListener()?.autoConnect = isEnabled } + fun setConnectOnBoot(isEnabled: Boolean) { + sharedPreferences.edit().putBoolean(IS_CONNECT_ON_BOOT_ENABLED_KEY, isEnabled).apply() + isConnectOnBootEnabled.value = isEnabled + } + fun setLocalNetworkSharing(isEnabled: Boolean) { serviceConnectionManager.settingsListener()?.allowLan = isEnabled } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt index 49d43e6b2770..fd89c850bc93 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt @@ -31,6 +31,7 @@ class VpnSettingsFragment : BaseFragment() { onRestoreMtuClick = vm::onRestoreMtuClick, onCancelMtuDialogClick = vm::onCancelDialogClick, onToggleAutoConnect = vm::onToggleAutoConnect, + onToggleConnectOnBoot = vm::onToggleConnectOnBoot, onToggleLocalNetworkSharing = vm::onToggleLocalNetworkSharing, onToggleDnsClick = vm::onToggleDnsClick, onToggleBlockAds = vm::onToggleBlockAds, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt index dfae3df53956..6a6fe15dd615 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt @@ -51,13 +51,16 @@ class VpnSettingsViewModel( private val dialogState = MutableStateFlow(null) private val vmState = - combine(repository.settingsUpdates, portRangeUseCase.portRanges(), dialogState) { - settings, - portRanges, - dialogState -> + combine( + repository.settingsUpdates, + portRangeUseCase.portRanges(), + dialogState, + repository.isConnectOnBootEnabled + ) { settings, portRanges, dialogState, isConnectOnBootEnabled -> VpnSettingsViewModelState( mtuValue = settings?.mtuString() ?: "", isAutoConnectEnabled = settings?.autoConnect ?: false, + isConnectOnBootEnabled = isConnectOnBootEnabled, isLocalNetworkSharingEnabled = settings?.allowLan ?: false, isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false, customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(), @@ -227,6 +230,10 @@ class VpnSettingsViewModel( viewModelScope.launch(dispatcher) { repository.setAutoConnect(isEnabled) } } + fun onToggleConnectOnBoot(isEnabled: Boolean) { + viewModelScope.launch(dispatcher) { repository.setConnectOnBoot(isEnabled) } + } + fun onToggleLocalNetworkSharing(isEnabled: Boolean) { viewModelScope.launch(dispatcher) { repository.setLocalNetworkSharing(isEnabled) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt index 2ebc2b397cac..675ed1354804 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt @@ -12,6 +12,7 @@ import net.mullvad.mullvadvpn.model.SelectedObfuscation data class VpnSettingsViewModelState( val mtuValue: String, val isAutoConnectEnabled: Boolean, + val isConnectOnBootEnabled: Boolean?, val isLocalNetworkSharingEnabled: Boolean, val isCustomDnsEnabled: Boolean, val isAllowLanEnabled: Boolean, @@ -27,6 +28,7 @@ data class VpnSettingsViewModelState( VpnSettingsUiState( mtuValue, isAutoConnectEnabled, + isConnectOnBootEnabled, isLocalNetworkSharingEnabled, isCustomDnsEnabled, customDnsList, @@ -46,6 +48,7 @@ data class VpnSettingsViewModelState( VpnSettingsViewModelState( mtuValue = EMPTY_STRING, isAutoConnectEnabled = false, + isConnectOnBootEnabled = null, isLocalNetworkSharingEnabled = false, isCustomDnsEnabled = false, customDnsList = listOf(), diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt index f8736eb823ea..d6e4167538c2 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt @@ -54,6 +54,7 @@ class VpnSettingsViewModelTest { @Before fun setUp() { every { mockSettingsRepository.settingsUpdates } returns mockSettingsUpdate + every { mockSettingsRepository.isConnectOnBootEnabled } returns MutableStateFlow(null) every { mockPortRangeUseCase.portRanges() } returns portRangeFlow viewModel = diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml index 0b690671ace7..52b7b30beaec 100644 --- a/android/lib/resource/src/main/res/values/strings.xml +++ b/android/lib/resource/src/main/res/values/strings.xml @@ -257,4 +257,5 @@ Connecting... Verifying purchase... Copied logs to clipboard + Connect on boot diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index 79d43d44b82e..ebcfec053a43 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -1705,6 +1705,9 @@ msgstr "" msgid "Changes to DNS related settings might not go into effect immediately due to cached results." msgstr "" +msgid "Connect on boot" +msgstr "" + msgid "Connecting..." msgstr ""