Merge branch 'main' into kari/bump

Signed-off-by: kari-ts <135075563+kari-ts@users.noreply.github.com>
pull/653/head
kari-ts 7 months ago committed by GitHub
commit 57c8d018ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -7,7 +7,6 @@ import android.app.Application
import android.app.Notification
import android.app.NotificationChannel
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
@ -37,11 +36,6 @@ import com.tailscale.ipn.ui.viewModel.VpnViewModel
import com.tailscale.ipn.ui.viewModel.VpnViewModelFactory
import com.tailscale.ipn.util.FeatureFlags
import com.tailscale.ipn.util.TSLog
import java.io.File
import java.io.IOException
import java.net.NetworkInterface
import java.security.GeneralSecurityException
import java.util.Locale
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@ -53,6 +47,11 @@ import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import libtailscale.Libtailscale
import java.io.File
import java.io.IOException
import java.net.NetworkInterface
import java.security.GeneralSecurityException
import java.util.Locale
class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
@ -470,25 +469,15 @@ open class UninitializedApp : Application() {
}
fun restartVPN() {
// Register a receiver to listen for the completion of stopVPN
val stopReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// Ensure stop intent is complete
if (intent?.action == IPNService.ACTION_STOP_VPN) {
// Unregister receiver after receiving the broadcast
context?.unregisterReceiver(this)
// Now start the VPN
startVPN()
}
}
val intent =
Intent(this, IPNService::class.java).apply { action = IPNService.ACTION_RESTART_VPN }
try {
startService(intent)
} catch (illegalStateException: IllegalStateException) {
TSLog.e(TAG, "restartVPN hit IllegalStateException in startService(): $illegalStateException")
} catch (e: Exception) {
TSLog.e(TAG, "restartVPN hit exception in startService(): $e")
}
// Register the receiver before stopping VPN
val intentFilter = IntentFilter(IPNService.ACTION_STOP_VPN)
this.registerReceiver(stopReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
stopVPN()
}
fun createNotificationChannel(id: String, name: String, description: String, importance: Int) {
@ -569,33 +558,13 @@ open class UninitializedApp : Application() {
return builder.build()
}
fun addUserDisallowedPackageName(packageName: String) {
if (packageName.isEmpty()) {
TSLog.e(TAG, "addUserDisallowedPackageName called with empty packageName")
return
}
getUnencryptedPrefs()
.edit()
.putStringSet(
DISALLOWED_APPS_KEY, disallowedPackageNames().toMutableSet().union(setOf(packageName)))
.apply()
this.restartVPN()
}
fun removeUserDisallowedPackageName(packageName: String) {
if (packageName.isEmpty()) {
TSLog.e(TAG, "removeUserDisallowedPackageName called with empty packageName")
fun updateUserDisallowedPackageNames(packageNames: List<String>) {
if (packageNames.any { it.isEmpty() }) {
TSLog.e(TAG, "updateUserDisallowedPackageNames called with empty packageName(s)")
return
}
getUnencryptedPrefs()
.edit()
.putStringSet(
DISALLOWED_APPS_KEY,
disallowedPackageNames().toMutableSet().subtract(setOf(packageName)))
.apply()
getUnencryptedPrefs().edit().putStringSet(DISALLOWED_APPS_KEY, packageNames.toSet()).apply()
this.restartVPN()
}

@ -12,12 +12,12 @@ import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.util.TSLog
import java.util.UUID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import libtailscale.Libtailscale
import java.util.UUID
open class IPNService : VpnService(), libtailscale.IPNService {
private val TAG = "IPNService"
@ -46,6 +46,13 @@ open class IPNService : VpnService(), libtailscale.IPNService {
close()
START_NOT_STICKY
}
ACTION_RESTART_VPN -> {
app.setWantRunning(false){
close()
app.startVPN()
}
START_NOT_STICKY
}
ACTION_START_VPN -> {
scope.launch { showForegroundNotification() }
app.setWantRunning(true)
@ -82,7 +89,6 @@ open class IPNService : VpnService(), libtailscale.IPNService {
}
override fun close() {
app.setWantRunning(false) {}
Notifier.setState(Ipn.State.Stopping)
disconnectVPN()
Libtailscale.serviceDisconnect(this)
@ -180,5 +186,6 @@ open class IPNService : VpnService(), libtailscale.IPNService {
companion object {
const val ACTION_START_VPN = "com.tailscale.ipn.START_VPN"
const val ACTION_STOP_VPN = "com.tailscale.ipn.STOP_VPN"
const val ACTION_RESTART_VPN = "com.tailscale.ipn.RESTART_VPN"
}
}

@ -105,7 +105,7 @@ class Tailcfg {
// isExitNode reproduces the Go logic in local.go peerStatusFromNode
val isExitNode: Boolean =
AllowedIPs?.contains("0.0.0.0/0") ?: false && AllowedIPs?.contains("::/0") ?: false
(AllowedIPs?.contains("0.0.0.0/0") ?: false) && (AllowedIPs?.contains("::/0") ?: false)
val isMullvadNode: Boolean
get() = Name.endsWith(".mullvad.ts.net.")

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ListItem
@ -26,6 +27,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import com.tailscale.ipn.R
@ -140,7 +142,11 @@ fun LoginView(
placeholder = {
Text(strings.placeholder, style = MaterialTheme.typography.bodySmall)
},
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.None))
keyboardOptions =
KeyboardOptions(
capitalization = KeyboardCapitalization.None, imeAction = ImeAction.Go),
keyboardActions =
KeyboardActions(onGo = { onSubmitAction(textVal) }))
})
ListItem(

@ -4,14 +4,18 @@
package com.tailscale.ipn.ui.viewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tailscale.ipn.App
import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.mdm.SettingState
import com.tailscale.ipn.ui.util.InstalledApp
import com.tailscale.ipn.ui.util.InstalledAppsManager
import com.tailscale.ipn.ui.util.set
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class SplitTunnelAppPickerViewModel : ViewModel() {
val installedAppsManager = InstalledAppsManager(packageManager = App.get().packageManager)
@ -20,6 +24,8 @@ class SplitTunnelAppPickerViewModel : ViewModel() {
val mdmExcludedPackages: StateFlow<SettingState<String?>> = MDMSettings.excludedPackages.flow
val mdmIncludedPackages: StateFlow<SettingState<String?>> = MDMSettings.includedPackages.flow
private var saveJob: Job? = null
init {
installedApps.set(installedAppsManager.fetchInstalledApps())
excludedPackageNames.set(
@ -30,15 +36,22 @@ class SplitTunnelAppPickerViewModel : ViewModel() {
}
fun exclude(packageName: String) {
if (excludedPackageNames.value.contains(packageName)) {
return
}
if (excludedPackageNames.value.contains(packageName)) return
excludedPackageNames.set(excludedPackageNames.value + packageName)
App.get().addUserDisallowedPackageName(packageName)
debounceSave()
}
fun unexclude(packageName: String) {
excludedPackageNames.set(excludedPackageNames.value - packageName)
App.get().removeUserDisallowedPackageName(packageName)
debounceSave()
}
private fun debounceSave() {
saveJob?.cancel()
saveJob =
viewModelScope.launch {
delay(500) // Wait to batch multiple rapid updates
App.get().updateUserDisallowedPackageNames(excludedPackageNames.value)
}
}
}

Loading…
Cancel
Save