android: fix vpn

-Move most of prepare and establish VPN logic out of Go into Android
-Fix prepareVPN argument to use request codes to differentiate sign in and prepare VPN
-Fix missing adapter implementation (setMtu)

Updates tailscale/corp#18202

Signed-off-by: kari-ts <kari@tailscale.com>
kari/fixvpn
kari-ts 2 months ago
parent 7b7f7254ba
commit 926abe56d6

@ -280,7 +280,7 @@ class App : Application(), libtailscale.AppContext {
Runnable {
val intent: Intent? = VpnService.prepare(act)
if (intent == null) {
Libtailscale.onVPNPrepared()
startVPN()
} else {
startActivityForResult(act, intent, reqCode)
}

@ -10,8 +10,8 @@ import android.os.Build
import android.system.OsConstants
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import java.util.UUID
import libtailscale.Libtailscale
import java.util.UUID
open class IPNService : VpnService(), libtailscale.IPNService {
private val randomID: String = UUID.randomUUID().toString()

@ -22,6 +22,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import androidx.navigation.navigation
import com.tailscale.ipn.Peer.RequestCodes
import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.theme.AppTheme
@ -43,12 +44,19 @@ import com.tailscale.ipn.ui.viewModel.SettingsNav
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.tailscale.ipn.App
class MainActivity : ComponentActivity() {
private var notifierScope: CoroutineScope? = null
private lateinit var requestVpnPermission: ActivityResultLauncher<Unit>
companion object {
// Request codes for Android callbacks.
// requestSignin is for Google Sign-In.
@JvmStatic val requestSignin: Int = 1000
// requestPrepareVPN is for when Android's VpnService.prepare completes.
@JvmStatic val requestPrepareVPN: Int = 1001
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -107,7 +115,8 @@ class MainActivity : ComponentActivity() {
}
lifecycleScope.launch {
Notifier.readyToPrepareVPN.collect { isReady ->
if (isReady) App.getApplication().prepareVPN(this@MainActivity, -1)
if (isReady)
App.getApplication().prepareVPN(this@MainActivity, RequestCodes.requestPrepareVPN)
}
}
}

@ -6,11 +6,32 @@ package com.tailscale.ipn;
import android.app.Fragment;
import android.content.Intent;
import libtailscale.Libtailscale;
public class Peer extends Fragment {
private static int resultOK = -1;
public class RequestCodes {
public static final int requestSignin = 1000;
public static final int requestPrepareVPN = 1001;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Libtailscale.onActivityResult(requestCode, resultCode, MaybeGoogle.getIdTokenForActivity(getActivity()));
switch (requestCode) {
case RequestCodes.requestSignin:
if (resultCode != resultOK) {
// TODO: send null Google token
break;
}
// TODO: send Google token
case RequestCodes.requestPrepareVPN:
if (resultCode == resultOK) {
App.getApplication().startVPN();
} else {
App.getApplication().setWantRunning(false);
// notify VPN revoked
}
}
}
}

@ -27,8 +27,8 @@ class VPNServiceBuilder(private val builder: VpnService.Builder) : libtailscale.
return builder.establish()?.let { ParcelFileDescriptor(it) }
}
override fun setMTU(p0: Long) {
TODO("Not yet implemented")
override fun setMTU(p0: Int) {
builder.setMtu(p0)
}
}

@ -69,14 +69,9 @@ object Notifier {
notify.LoginFinished?.let { loginFinished.set(it.property) }
notify.Version?.let(version::set)
}
var previousState: Ipn.State? = null
state.collect { currstate ->
readyToPrepareVPN.set(
previousState != null &&
previousState!! <= Ipn.State.Stopped &&
currstate > Ipn.State.Stopped)
readyToPrepareVPN.set(currstate > Ipn.State.Stopped)
tileReady.set(currstate >= Ipn.State.Stopped)
previousState = currstate
}
Log.d(TAG, "Stopped")
}

@ -33,18 +33,6 @@ var (
onDNSConfigChanged = make(chan struct{}, 1)
)
const (
// Request codes for Android callbacks.
// requestSignin is for Google Sign-In.
requestSignin = 1000 + iota
// requestPrepareVPN is for when Android's VpnService.prepare
// completes.
requestPrepareVPN
)
// resultOK is Android's Activity.RESULT_OK.
const resultOK = -1
func OnShareIntent(nfiles int32, types []int32, mimes []string, items []string, names []string, sizes []int) {
// TODO(oxtoacart): actually implement this
// const (

@ -65,7 +65,7 @@ type IPNService interface {
// VPNServiceBuilder corresponds to Android's VpnService.Builder.
type VPNServiceBuilder interface {
SetMTU(int) error
SetMTU(int32) error
AddDNSServer(string) error
AddSearchDomain(string) error
AddRoute(string, int32) error
@ -120,10 +120,6 @@ type InputStream interface {
// The below are global callbacks that allow the Java application to notify Go
// of various state changes.
func OnVPNPrepared() {
notifyVPNPrepared()
}
func RequestVPN(service IPNService) {
onVPNRequested <- service
}
@ -131,21 +127,3 @@ func RequestVPN(service IPNService) {
func ServiceDisconnect(service IPNService) {
onDisconnect <- service
}
func OnActivityResult(reqCode, resCode int, idToken string) {
switch reqCode {
case requestSignin:
if resCode != resultOK {
onGoogleToken <- ""
break
}
onGoogleToken <- idToken
case requestPrepareVPN:
if resCode == resultOK {
notifyVPNPrepared()
} else {
notifyVPNClosed()
notifyVPNRevoked()
}
}
}

Loading…
Cancel
Save