diff --git a/cmd/tailscale/backend.go b/cmd/tailscale/backend.go index 08b7d83..f5306c4 100644 --- a/cmd/tailscale/backend.go +++ b/cmd/tailscale/backend.go @@ -81,6 +81,11 @@ var googleDNSServers = []netip.Addr{ // VPN status was revoked. var errVPNNotPrepared = errors.New("VPN service not prepared or was revoked") +// errMultipleUsers is used when we get a "INTERACT_ACROSS_USERS" error, which +// happens due to a bug in Android. See: +// https://github.com/tailscale/tailscale/issues/2180 +var errMultipleUsers = errors.New("VPN cannot be created on this device due to an Android bug with multiple users") + func newBackend(dataDir string, jvm *jni.JVM, appCtx jni.Object, store *stateStore, settings settingsFunc) (*backend, error) { @@ -266,6 +271,9 @@ func (b *backend) updateTUN(service jni.Object, rcfg *router.Config, dcfg *dns.O establish := jni.GetMethodID(env, bcls, "establish", "()Landroid/os/ParcelFileDescriptor;") parcelFD, err := jni.CallObjectMethod(env, builder, establish) if err != nil { + if strings.Contains(err.Error(), "INTERACT_ACROSS_USERS") { + return errMultipleUsers + } return fmt.Errorf("VpnService.Builder.establish: %v", err) } if parcelFD == 0 { diff --git a/cmd/tailscale/main.go b/cmd/tailscale/main.go index 16efe85..4d18283 100644 --- a/cmd/tailscale/main.go +++ b/cmd/tailscale/main.go @@ -368,6 +368,9 @@ func (a *App) runBackend() error { if service != 0 { if cfg.rcfg != nil && state.State >= ipn.Starting { if err := b.updateTUN(service, cfg.rcfg, cfg.dcfg); err != nil { + if errors.Is(err, errMultipleUsers) { + a.pushNotify(service, "Multiple Users", multipleUsersText) + } log.Printf("VPN update failed: %v", err) notifyVPNClosed() } @@ -1405,3 +1408,6 @@ func randHex(n int) string { rand.Read(b) return hex.EncodeToString(b) } + +const multipleUsersText = "Tailscale can't start due to an Android bug when multiple users are present on this device. " + + "Please see https://tailscale.com/s/multi-user-bug for more information."