golang/go

x/mobile: Calling net.InterfaceAddrs() fails on Android SDK 30

halseth opened this issue Β· 104 comments

What version of Go are you using (go version)?

$ go version
go version go1.14.6 darwin/amd64

$gomobile version
gomobile version +973feb4 Sat Aug 1 11:21:45 2020 +0000 (android,ios); androidSDK=/sdk/platforms/android-30

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/user/Library/Caches/go-build"
GOENV="/Users/user/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/user/golang"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/Cellar/go/1.14.6/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.14.6/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/user/golang/src/golang.org/x/mobile/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/kq/3436m_v11sg0l7zqtmv2r1gw0000gn/T/go-build713467523=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Calling net.InterfaceAddrs() fails on Android app targetting SDK version 30. With build.gradle looking like:

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.0"
    defaultConfig {
        applicationId "com.example.testapp"
        minSdkVersion 29
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

Building the exact same app targetting SDK 29 works, and returns no error:

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.0"
    defaultConfig {
        applicationId "com.example.testapp"
        minSdkVersion 29
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

What did you expect to see?

Targetting SDK 30 would behave no differently.

What did you see instead?

Calling net.InterfaceAddrs() results in error route ip+net: netlinkrib: permission denied when embedded in Android app targetting SDK 30 (R):

2020-08-04 15:10:21.386 15754-15754/? W/Thread-2: type=1400 audit(0.0:616): avc: denied { bind } for scontext=u:r:untrusted_app:s0:c158,c256,c512,c768 tcontext=u:r:untrusted_app:s0:c158,c256,c512,c768 tclass=netlink_route_socket permissive=0 b/155595000 app=com.example.testapp

Any more updates on this one? Android 11 was released today.

Any updates?

I'd also love an update, if possible. This is blocking a project of mine right now.

Anybody looking into this?

bt90 commented

Seems to be caused by these new restrictions in Android 11:

The following is a list of the ways that apps are affected by this change:

  • NetworkInterface.getHardwareAddress() returns null for every interface.
  • Apps cannot use the bind() function on NETLINK_ROUTE sockets.
  • The ip command does not return information about interfaces.
  • Apps cannot send RTM_GETLINK messages.

https://developer.android.com/training/articles/user-data-ids#mac-11-plus

https://developer.android.com/distribute/best-practices/develop/target-sdk

In August 2021, new apps in the Google Play Store will be required to target API level 30.
In November 2021, updates to existing apps in the Google Play Store will be required to target API level 30.

Any updates on this one? This fix did not work for us: Catfriend1/syncthing-android#800

Hello golang dev team, any update on this issue? This is blocking the Android 11 compatibility of my app :/. Thanks!

We ended up developing an alternative using Android APIs, which we call instead of getInterfaceAddrs().
tailscale/tailscale-android#21

andob commented

hello, any update on this?

I'm looking to use a go library inside an android app targeting API level 32. Code crashes on devices with API level >= 30 because of this reason (route ip+net: netlinkrib: permission denied).

I could downgrade to 29, but it's a hack. Go runtime should implement a workaround to fix the issue.

Here is how I fix this x/mobile: Calling net.InterfaceAddrs() fails on Android SDK 30 issue to resolve IPFS in mobile issue like route ip+net: netlinkrib: permission denied with x/mobile/cmd/gomobile as compile tools , ref to cmd/tailscale: implement getInterfaces + SDK 30 which use gioui.org as compile tools.

  • In MainApplication.java

Take react native project for example, in YOUR_PROJECT/android/app/src/main/AndroidManifest.xml

    <application
      android:name=".MainApplication"

so be the MainApplication.java here.

import go.Seq;
...
  @Override
  public void onCreate() {
    super.onCreate();
    ...

    // setContext here, so that if RunOnJVM() with golang.org/x/mobile/app to call JAVA from GO,
    // will not cause error "no current JVM"
    Seq.setContext(getApplicationContext());
  }
...
}

And also in MainApplication.java

import java.lang.StringBuilder;

import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
...
  // To fix [x/mobile: Calling net.InterfaceAddrs() fails on Android SDK 30](https://github.com/golang/go/issues/40569)
  // Ref to getInterfaces() in https://github.com/tailscale/tailscale-android/pull/21/files
  //
  // Returns details of the interfaces in the system, encoded as a single string for ease
  // of JNI transfer over to the Go environment.
  //
  // Example:
  // rmnet_data0 10 2000 true false false false false | fe80::4059:dc16:7ed3:9c6e%rmnet_data0/64
  // dummy0 3 1500 true false false false false | fe80::1450:5cff:fe13:f891%dummy0/64
  // wlan0 30 1500 true true false false true | fe80::2f60:2c82:4163:8389%wlan0/64 10.1.10.131/24
  // r_rmnet_data0 21 1500 true false false false false | fe80::9318:6093:d1ad:ba7f%r_rmnet_data0/64
  // rmnet_data2 12 1500 true false false false false | fe80::3c8c:44dc:46a9:9907%rmnet_data2/64
  // r_rmnet_data1 22 1500 true false false false false | fe80::b6cd:5cb0:8ae6:fe92%r_rmnet_data1/64
  // rmnet_data1 11 1500 true false false false false | fe80::51f2:ee00:edce:d68b%rmnet_data1/64
  // lo 1 65536 true false true false false | ::1/128 127.0.0.1/8
  // v4-rmnet_data2 68 1472 true true false true true | 192.0.0.4/32
  //
  // Where the fields are:
  // name ifindex mtu isUp hasBroadcast isLoopback isPointToPoint hasMulticast | ip1/N ip2/N ip3/N;
  String getInterfacesAsString() {
    List<NetworkInterface> interfaces;
    try {
      interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
    } catch (Exception e) {
      return "";
    }

    StringBuilder sb = new StringBuilder("");
    for (NetworkInterface nif : interfaces) {
      try {
        // Android doesn't have a supportsBroadcast() but the Go net.Interface wants
        // one, so we say the interface has broadcast if it has multicast.
        sb.append(String.format("%s %d %d %b %b %b %b %b |", nif.getName(),
                       nif.getIndex(), nif.getMTU(), nif.isUp(), nif.supportsMulticast(),
                       nif.isLoopback(), nif.isPointToPoint(), nif.supportsMulticast()));

        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
          // InterfaceAddress == hostname + "/" + IP
          String[] parts = ia.toString().split("/", 0);
          if (parts.length > 1) {
            sb.append(String.format("%s/%d ", parts[1], ia.getNetworkPrefixLength()));
          }
        }
      } catch (Exception e) {
        // TODO(dgentry) should log the exception not silently suppress it.
        continue;
      }
      sb.append("\n");
    }

    return sb.toString();
  }
  • In go.mod

Add

	git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0
	inet.af/netaddr v0.0.0-20210721214506-ce7a8ad02cc1
  • In CLI
go mod download

Above will download git.wow.st/gmp/jni and inet.af/netaddr into ~/go/pkg/mod/ , and modify go.mod automatically.

go mod vendor

Above will remove vendor/ automatically then copy again from ~/go/pkg/mod/ .

If go mod download modified go.mod into

	git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0 // indirect
	inet.af/netaddr v0.0.0-20210721214506-ce7a8ad02cc1 // indirect

then they will not be copied into vendor/ by go mod vendor , so you can temporarily add

	"git.wow.st/gmp/jni"
	"inet.af/netaddr"

into one of your own .go file, run go mod download again, found // indirect disappeared, reset your own .go file, run go mod vendor again, found they were copied into vendor/ .

  • In vendor/github.com/multiformats/go-multiaddr/net/net.go

Replace

import (
	"context"
	"fmt"
	"net"

	ma "github.com/multiformats/go-multiaddr"
)

with

import (
	"context"
	"errors"
	"fmt"
	"net"
	"runtime"
	"strings"

	"git.wow.st/gmp/jni"
	"golang.org/x/mobile/app"
	"inet.af/netaddr"

	ma "github.com/multiformats/go-multiaddr"
)

And also in vendor/github.com/multiformats/go-multiaddr/net/net.go, replace

// InterfaceMultiaddrs will return the addresses matching net.InterfaceAddrs
func InterfaceMultiaddrs() ([]ma.Multiaddr, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}

	maddrs := make([]ma.Multiaddr, len(addrs))
	for i, a := range addrs {
		maddrs[i], err = FromNetAddr(a)
		if err != nil {
			return nil, err
		}
	}
	return maddrs, nil
}

with

// InterfaceMultiaddrs will return the addresses matching net.InterfaceAddrs
func InterfaceMultiaddrs() ([]ma.Multiaddr, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		if runtime.GOOS == "android" {
			// To fix [x/mobile: Calling net.InterfaceAddrs() fails on Android SDK 30](https://github.com/golang/go/issues/40569)
			addrs, err = getInterfaceAddrsFromAndroid()
			if err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}

	maddrs := make([]ma.Multiaddr, len(addrs))
	for i, a := range addrs {
		maddrs[i], err = FromNetAddr(a)
		if err != nil {
			return nil, err
		}
	}
	return maddrs, nil
}

// Ref to getInterfaces() in https://github.com/tailscale/tailscale-android/pull/21/files
func getInterfaceAddrsFromAndroid() ([]net.Addr, error) {
	var ifaceString string

	// if use "gioui.org/app", ref to jni.Do() in https://github.com/tailscale/tailscale-android/pull/21/files

	// if use "golang.org/x/mobile/app", use app.RunOnJVM() below
	err := app.RunOnJVM(func(vm, env, ctx uintptr) error {
		jniEnv := jni.EnvFor(env)

		// cls := jni.FindClass(jniEnv, "YOUR/PACKAGE/NAME/CLASSNAME")
		// m := jni.GetMethodID(jniEnv, cls, "getInterfacesAsString", "()Ljava/lang/String;")
		// n, err := jni.CallStaticObjectMethod(jniEnv, cls, m)

		// above `YOUR.PACKAGE.NAME` `CLASSNAME.java` sometimes will cause strange [java.lang.ClassNotFoundException: Didn't find class on path: dexpathlist](https://stackoverflow.com/questions/22399572/java-lang-classnotfoundexception-didnt-find-class-on-path-dexpathlist)
		// so use below `MainApplication.java` comes from `<application android:name=".MainApplication"` in `YOUR_PROJECT/android/app/src/main/AndroidManifest.xml`

		appCtx := jni.Object(ctx)
		cls := jni.GetObjectClass(jniEnv, appCtx)
		m := jni.GetMethodID(jniEnv, cls, "getInterfacesAsString", "()Ljava/lang/String;")
		n, err := jni.CallObjectMethod(jniEnv, appCtx, m)

		if err != nil {
			return errors.New("getInterfacesAsString Method invocation failed")
		}
		ifaceString = jni.GoString(jniEnv, jni.String(n))
		return nil
	})

	if err != nil {
		return nil, err
	}

	var ifat []net.Addr
	for _, iface := range strings.Split(ifaceString, "\n") {
		// Example of the strings we're processing:
		// wlan0 30 1500 true true false false true | fe80::2f60:2c82:4163:8389%wlan0/64 10.1.10.131/24
		// r_rmnet_data0 21 1500 true false false false false | fe80::9318:6093:d1ad:ba7f%r_rmnet_data0/64
		// mnet_data2 12 1500 true false false false false | fe80::3c8c:44dc:46a9:9907%rmnet_data2/64

		if strings.TrimSpace(iface) == "" {
			continue
		}

		fields := strings.Split(iface, "|")
		if len(fields) != 2 {
			// log.Printf("getInterfaces: unable to split %q", iface)
			continue
		}

		addrs := strings.Trim(fields[1], " \n")
		for _, addr := range strings.Split(addrs, " ") {
			ip, err := netaddr.ParseIPPrefix(addr)
			if err == nil {
				ifat = append(ifat, ip.IPNet())
			}
		}
	}

		return ifat, nil
}
  • In CLI
    export ANDROID_NDK_HOME=~/tools/android-sdk/ndk/21.4.7075529

or

    cd ~/tools/android-sdk/ndk
    ln -s 21.4.7075529 ndk-bundle

21.4.7075529 is default in RN 0.66 and gradle 6.7.1

If r22 or higher, will cause

# golang.org/x/mobile/app
ld: error: duplicate symbol: display

just like x/mobile/cmd/gomobile: gomobile build on simple program returns "ld: error: duplicate symbol: x_cgo_inittls"

Finally, you can continue your gomobile bind work flow πŸ˜‹

PS: notice java.util.Locale.ROOT as described below #40569 (comment)

For anyone using the technique of having Java code pass a string with an interface list over to the Go code, we did find an issue after some time in the field.

For an Android phone in a Locale like Saudi Arabia which uses Hindu-Arabic numerals, the Java code will automatically format the decimal numbers using the locale-appropriate glyphs:

lo Ω‘ Ω¦Ω₯Ω₯Ω£Ω¦ true false true false false |

The receiving Go code cannot handle this. As these strings are purely internal to pass information between the Java runtime and Go environment, they will never be visible to the user. We addressed it by setting the locale in the Java code to always format the string in a way which the Go code can handle:

sb.append(String.format(java.util.Locale.ROOT, "%s %d %d %b %b %b %b %b |", nif.getName(),

The full PR to address it is in tailscale/tailscale-android@fd42b4b, the App.java file contains the relevant changes. We had also used a similar technique for DNS which required a similar fix but is not likely used in your system.

I did something similar for IPFS on Android as well, the project is https://github.com/AgregoreWeb/agregore-ipfs-daemon

Android Java code is here: https://github.com/AgregoreWeb/agregore-ipfs-daemon/blob/2a0798197769772be810947f2451c10fca03d1ca/get_interfaces.java

Parsing of interfaces string from Java is here: https://github.com/AgregoreWeb/agregore-ipfs-daemon/blob/2a0798197769772be810947f2451c10fca03d1ca/gateway/interfaces.go#L24

FYI @makeworld-the-better-one, in your code you replace the address parsing func: instead of using netaddr.ParseIPPrefix() you use net.ParseCIDR(). In my testing I found it did not give the same/desired result, however.
For example parsing net.ParseCIDR("10.150.6.112/16") results in 10.150.0.0/16.

I was able to work around this issue by calling getifaddrs via cgo, which was preferable to using JNI for me. getifaddrs is available in Android 7.0+.

Code in the gist here replicates most but not all of the stdlib functionality. Some data isn't populated.
https://gist.github.com/iamcalledrob/67b710b1ca09465b906f04b91bb56e1f

Here is how I fix this x/mobile: Calling net.InterfaceAddrs() fails on Android SDK 30 issue to resolve IPFS in mobile issue like route ip+net: netlinkrib: permission denied with x/mobile/cmd/gomobile as compile tools , ref to cmd/tailscale: implement getInterfaces + SDK 30 which use gioui.org as compile tools.

  • In MainApplication.java

Take react native project for example, in YOUR_PROJECT/android/app/src/main/AndroidManifest.xml

    <application
      android:name=".MainApplication"

so be the MainApplication.java here.

import go.Seq;

@flyskywhy Hello there! I'm trying to solve the issue with your solution but Android Studio is giving me an error when importing go.Seq, maybe a stupid question but could not find an answer on google... how did you import the go.Seq dependence?

I was able to work around this issue by calling getifaddrs via cgo

Brilliant! Inspired by your work, I looked into how Android implements getifaddrs, and found that RTM_GETADDR is used for non-system apps as RTM_GETLINK is forbidden [1]. On the other hand, there are already some code using RTM_GETADDR in Go [2]. I collected relevant functions, removed usage of forbidden RTM_GETLINK and got a working version:

Pure-Go InterfaceAddrs() for newer Android
import (
	"net"
	"os"
	"syscall"
	"unsafe"
)

// NetlinkRouteRequest represents a request message to receive routing
// and link states from the kernel.
type NetlinkRouteRequest struct {
	Header syscall.NlMsghdr
	Data   syscall.RtGenmsg
}

func (rr *NetlinkRouteRequest) toWireFormat() []byte {
	b := make([]byte, rr.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid
	b[16] = byte(rr.Data.Family)
	return b
}

func newNetlinkRouteRequest(proto, seq, family int) []byte {
	rr := &NetlinkRouteRequest{}
	rr.Header.Len = uint32(syscall.NLMSG_HDRLEN + syscall.SizeofRtGenmsg)
	rr.Header.Type = uint16(proto)
	rr.Header.Flags = syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST
	rr.Header.Seq = uint32(seq)
	rr.Data.Family = uint8(family)
	return rr.toWireFormat()
}

// NetlinkRIB returns routing information base, as known as RIB, which
// consists of network facility information, states and parameters.
func NetlinkRIB(proto, family int) ([]byte, error) {
	s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, syscall.NETLINK_ROUTE)
	if err != nil {
		return nil, err
	}
	defer syscall.Close(s)
	sa := &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
	wb := newNetlinkRouteRequest(proto, 1, family)
	if err := syscall.Sendto(s, wb, 0, sa); err != nil {
		return nil, err
	}
	lsa, err := syscall.Getsockname(s)
	if err != nil {
		return nil, err
	}
	lsanl, ok := lsa.(*syscall.SockaddrNetlink)
	if !ok {
		return nil, syscall.EINVAL
	}
	var tab []byte
	rbNew := make([]byte, syscall.Getpagesize())
done:
	for {
		rb := rbNew
		nr, _, err := syscall.Recvfrom(s, rb, 0)
		if err != nil {
			return nil, err
		}
		if nr < syscall.NLMSG_HDRLEN {
			return nil, syscall.EINVAL
		}
		rb = rb[:nr]
		tab = append(tab, rb...)
		msgs, err := syscall.ParseNetlinkMessage(rb)
		if err != nil {
			return nil, err
		}
		for _, m := range msgs {
			if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid {
				return nil, syscall.EINVAL
			}
			if m.Header.Type == syscall.NLMSG_DONE {
				break done
			}
			if m.Header.Type == syscall.NLMSG_ERROR {
				return nil, syscall.EINVAL
			}
		}
	}
	return tab, nil
}


func newAddr(ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) net.Addr {
	var ipPointToPoint bool
	// Seems like we need to make sure whether the IP interface
	// stack consists of IP point-to-point numbered or unnumbered
	// addressing.
	for _, a := range attrs {
		if a.Attr.Type == syscall.IFA_LOCAL {
			ipPointToPoint = true
			break
		}
	}
	for _, a := range attrs {
		if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS {
			continue
		}
		switch ifam.Family {
		case syscall.AF_INET:
			return &net.IPNet{IP: net.IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv4len)}
		case syscall.AF_INET6:
			ifa := &net.IPNet{IP: make(net.IP, net.IPv6len), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv6len)}
			copy(ifa.IP, a.Value[:])
			return ifa
		}
	}
	return nil
}

func addrTable(msgs []syscall.NetlinkMessage) ([]net.Addr, error) {
	var ifat []net.Addr
loop:
	for _, m := range msgs {
		switch m.Header.Type {
		case syscall.NLMSG_DONE:
			break loop
		case syscall.RTM_NEWADDR:
			ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
			attrs, err := syscall.ParseNetlinkRouteAttr(&m)
			if err != nil {
				return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
			}
			ifa := newAddr(ifam, attrs)
			if ifa != nil {
				ifat = append(ifat, ifa)
			}
		}
	}
	return ifat, nil
}

func interfaceAddrTable() ([]net.Addr, error) {
	tab, err := NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
	if err != nil {
		return nil, os.NewSyscallError("netlinkrib", err)
	}
	msgs, err := syscall.ParseNetlinkMessage(tab)
	if err != nil {
		return nil, os.NewSyscallError("parsenetlinkmessage", err)
	}
	ifat, err := addrTable(msgs)
	if err != nil {
		return nil, err
	}
	return ifat, nil
}

func InterfaceAddrs() ([]net.Addr, error) {
	ifat, err := interfaceAddrTable()
	if err != nil {
		err = &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
	}
	return ifat, err
}

[1] https://android.googlesource.com/platform/bionic/+/refs/tags/android-13.0.0_r18/libc/bionic/ifaddrs.cpp#315
[2] https://github.com/golang/go/blob/go1.20rc1/src/net/interface_linux.go#L124

I was able to work around this issue by calling getifaddrs via cgo

Brilliant! Inspired by your work, I looked into how Android implements getifaddrs, and found that RTM_GETADDR is used for non-system apps as RTM_GETLINK is forbidden [1]. On the other hand, there are already some code using RTM_GETADDR in Go [2]. I collected relevant functions, removed usage of forbidden RTM_GETLINK and got a working version:

Pure-Go InterfaceAddrs() for newer Android
[1] https://android.googlesource.com/platform/bionic/+/refs/tags/android-13.0.0_r18/libc/bionic/ifaddrs.cpp#315 [2] https://github.com/golang/go/blob/go1.20rc1/src/net/interface_linux.go#L124

I do it samething follew your ,but it does't work on ipfs kubo.

@yan12125 I haven't tested your code, but this approach looks great.

Unfortunately any of our workarounds don't work when using 3rd party libraries that call net.Interfaces() directly. This is a bit of a showstopper for a lot of networking code on Android.

I wonder if the net/interface pkg in stdlib can be updated to use these different syscalls for Android?

it does't work on ipfs kubo.

Any error meesages?


I wonder if the net/interface pkg in stdlib can be updated to use these different syscalls for Android?

I believe so. My example is modified from existing functions in stdlib, so those modifications should be compatible with stdlib. I can give it a try and create a pull request after finding time on reading contribution guidelines.

Sorry I made a mistake in my previous example - I tested on an app targeting older SDK. I updated the example after testing on newer SDK.

The key difference in the updated example is that I copied codes from syscall.NetlinkRIB and remove the Bind call on the netlink socket, which is forbidden by new Android. I assume binding is not necessary for net.InterfaceAddrs() , but maybe necessary for some other scenarios. I'm not sure how to integrate such a change into Go stdlib. Maybe a new system call or a new argument for syscall.NetlinkRIB?

Sorry I made a mistake in my previous example - I tested on an app targeting older SDK. I updated the example after testing on newer SDK.

The key difference in the updated example is that I copied codes from syscall.NetlinkRIB and remove the Bind call on the netlink socket, which is forbidden by new Android. I assume binding is not necessary for net.InterfaceAddrs() , but maybe necessary for some other scenarios. I'm not sure how to integrate such a change into Go stdlib. Maybe a new system call or a new argument for syscall.NetlinkRIB?

Would be great if you could do the PR. Wishing you the best of luck!

Thanks! But I'm not sure how to integrate those changes into stdlib in an elegant way as I've mentioned above.

Sorry I made a mistake in my previous example - I tested on an app targeting older SDK. I updated the example after testing on newer SDK.

The key difference in the updated example is that I copied codes from syscall.NetlinkRIB and remove the Bind call on the netlink socket, which is forbidden by new Android. I assume binding is not necessary for net.InterfaceAddrs() , but maybe necessary for some other scenarios. I'm not sure how to integrate such a change into Go stdlib. Maybe a new system call or a new argument for syscall.NetlinkRIB?

Could you provide more detail of remove bind?

Could you provide more detail of remove bind?

I removed https://github.com/golang/go/blob/go1.20rc1/src/syscall/netlink_linux.go#L59-L61, which violate the second rule mentioned at #40569 (comment):

  • Apps cannot use the bind() function on NETLINK_ROUTE sockets.

As a result, the code fails on newer Android.

any update?

I did resovled this problem.
No need java code, only need change some code of golang lib source code.

My android app intergrated ipfs ,but can not run on Android 13, this problem bother me for serveral months,
I did resolve this problem yesterday.

@yiweichi , would you please explain how you did it? I'm trying with several proposals from others here but still struggling to make it work. I would like to have a NO JAVA code solution like you did :)

Brilliant! Inspired by your work, I looked into how Android implements getifaddrs, and found that RTM_GETADDR is used for non-system apps as RTM_GETLINK is forbidden [1]. On the other hand, there are already some code using RTM_GETADDR in Go [2]. I collected relevant functions, removed usage of forbidden RTM_GETLINK and got a working version:

Pure-Go InterfaceAddrs() for newer Android
[1] https://android.googlesource.com/platform/bionic/+/refs/tags/android-13.0.0_r18/libc/bionic/ifaddrs.cpp#315 [2] https://github.com/golang/go/blob/go1.20rc1/src/net/interface_linux.go#L124

@yan12125 You're calling linux specific syscall constants (processor specific too I think). Can't work with this code on macOS (darwin syscall is different). There should be a platform independent way to implement this logic.

Brilliant! Inspired by your work, I looked into how Android implements getifaddrs, and found that RTM_GETADDR is used for non-system apps as RTM_GETLINK is forbidden [1]. On the other hand, there are already some code using RTM_GETADDR in Go [2]. I collected relevant functions, removed usage of forbidden RTM_GETLINK and got a working version:
Pure-Go InterfaceAddrs() for newer Android
[1] https://android.googlesource.com/platform/bionic/+/refs/tags/android-13.0.0_r18/libc/bionic/ifaddrs.cpp#315 [2] https://github.com/golang/go/blob/go1.20rc1/src/net/interface_linux.go#L124

@yan12125 You're calling linux specific syscall constants (processor specific too I think). Can't work with this code on macOS (darwin syscall is different). There should be a platform independent way to implement this logic.

It should work. Building Android apps on macOS will use Linux codes instead of Darwin ones.

@yan12125 I need to run and test the go library separately, this go code doesn't compile on macOS. Is there a tag I can add to avoid using this code when compiling in darwin.

You probably want something like // +build linux.

@yan12125 Doesn't compile in editor (vscode) with build tags. How can I modify the go/src/net and /syscall libraries in my project like you did in Work-around ?

My android app intergrated ipfs ,but can not run on Android 13, this problem bother me for serveral months, I did resolve this problem yesterday.

can you please share your solution on how you resolved it?

@yan12125 Doesn't compile in editor (vscode) with build tags. How can I modify the go/src/net and /syscall libraries in my project like you did in Work-around ?

That commit is for go itself, not applications. Please check InterfaceAddrs function in #40569 (comment) instead.

@yan12125 Can't use InterfaceAddrs function you shared in comment. I need to compile and test my go code on macOS before I can use it in my android app .
Can you open/ ask for a pull request for the changes in work-around ?

In my test I saw that even if you set targetSdkVersion to 29, you'll still get the "route ip+net: netlinkrib: permission denied" error in Android 13+. Previous Android versions work OK.

Mc231 commented

Any updates?

Hello @yan12125 , I'm finally following your recommendation, downloaded go source code and modified both the netling_linux (removing the bind call) and replaced the content from interface_linux with your code, but I'm having the following issue while compiling from source in macOS:

ok  	go/ast	0.349s
--- FAIL: TestDependencies (1.32s)
    deps_test.go:639: unexpected dependency: net imports [net]
FAIL
FAIL	go/build	1.769s

Looks like the test is having a conflict with the net import. Did you do anything else beyond modifying such files?

Thanks!

@simbadMarino could you try this patch? It works with Go 1.21 rc1 source.

diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go
index 9112ecc854c74..02b5a6047ed93 100644
--- a/src/net/interface_linux.go
+++ b/src/net/interface_linux.go
@@ -6,6 +6,7 @@ package net
 
 import (
 	"os"
+	"runtime"
 	"syscall"
 	"unsafe"
 )
@@ -133,7 +134,7 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
 	if ifi == nil {
 		var err error
 		ift, err = interfaceTable(0)
-		if err != nil {
+		if err != nil && (runtime.GOOS != "android" || err != syscall.EACCES) {
 			return nil, err
 		}
 	}
@@ -153,6 +154,18 @@ loop:
 			break loop
 		case syscall.RTM_NEWADDR:
 			ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
+			if len(ift) == 0 && ifi == nil {
+				// if len(ift) == 0, the restriction on Android API 30+ applies, and RTM_GETLINK messages are prohibited
+				// Only querying all interfaces (ifi == nil) makes sense in this case.
+				attrs, err := syscall.ParseNetlinkRouteAttr(&m)
+				if err != nil {
+					return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
+				}
+				ifa := newAddr(ifam, attrs)
+				if ifa != nil {
+					ifat = append(ifat, ifa)
+				}
+			}
 			if len(ift) != 0 || ifi.Index == int(ifam.Index) {
 				if len(ift) != 0 {
 					var err error
diff --git a/src/syscall/netlink_linux.go b/src/syscall/netlink_linux.go
index a503a0744005b..c4fba399a710d 100644
--- a/src/syscall/netlink_linux.go
+++ b/src/syscall/netlink_linux.go
@@ -7,6 +7,7 @@
 package syscall
 
 import (
+	"runtime"
 	"sync"
 	"unsafe"
 )
@@ -65,7 +66,9 @@ func NetlinkRIB(proto, family int) ([]byte, error) {
 	defer Close(s)
 	sa := &SockaddrNetlink{Family: AF_NETLINK}
 	if err := Bind(s, sa); err != nil {
-		return nil, err
+		if runtime.GOOS != "android" || err != EACCES {
+			return nil, err
+		}
 	}
 	wb := newNetlinkRouteRequest(proto, 1, family)
 	if err := Sendto(s, wb, 0, sa); err != nil {

@simbadMarino could you try this patch? It works with Go 1.21 rc1 source.

diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go
index 9112ecc854c74..02b5a6047ed93 100644
--- a/src/net/interface_linux.go
+++ b/src/net/interface_linux.go
@@ -6,6 +6,7 @@ package net
 
 import (
 	"os"
+	"runtime"
 	"syscall"
 	"unsafe"
 )
@@ -133,7 +134,7 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
 	if ifi == nil {
 		var err error
 		ift, err = interfaceTable(0)
-		if err != nil {
+		if err != nil && (runtime.GOOS != "android" || err != syscall.EACCES) {
 			return nil, err
 		}
 	}
@@ -153,6 +154,18 @@ loop:
 			break loop
 		case syscall.RTM_NEWADDR:
 			ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
+			if len(ift) == 0 && ifi == nil {
+				// if len(ift) == 0, the restriction on Android API 30+ applies, and RTM_GETLINK messages are prohibited
+				// Only querying all interfaces (ifi == nil) makes sense in this case.
+				attrs, err := syscall.ParseNetlinkRouteAttr(&m)
+				if err != nil {
+					return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
+				}
+				ifa := newAddr(ifam, attrs)
+				if ifa != nil {
+					ifat = append(ifat, ifa)
+				}
+			}
 			if len(ift) != 0 || ifi.Index == int(ifam.Index) {
 				if len(ift) != 0 {
 					var err error
diff --git a/src/syscall/netlink_linux.go b/src/syscall/netlink_linux.go
index a503a0744005b..c4fba399a710d 100644
--- a/src/syscall/netlink_linux.go
+++ b/src/syscall/netlink_linux.go
@@ -7,6 +7,7 @@
 package syscall
 
 import (
+	"runtime"
 	"sync"
 	"unsafe"
 )
@@ -65,7 +66,9 @@ func NetlinkRIB(proto, family int) ([]byte, error) {
 	defer Close(s)
 	sa := &SockaddrNetlink{Family: AF_NETLINK}
 	if err := Bind(s, sa); err != nil {
-		return nil, err
+		if runtime.GOOS != "android" || err != EACCES {
+			return nil, err
+		}
 	}
 	wb := newNetlinkRouteRequest(proto, 1, family)
 	if err := Sendto(s, wb, 0, sa); err != nil {

Thanks my friend, will give it a try tonight!

@yan12125 looks like its working! I can finally run BTFS on Android which is a go-ipfs fork, meaning those willing to run ipfs on android might find this patch handy, I tested with targetSdkVersion =30 & 33 and so far so good, thanks so much!! :)

@yan12125 looks like its working! I can finally run BTFS on Android which is a go-ipfs fork, meaning those willing to run ipfs on android might find this patch handy, I tested with targetSdkVersion =30 & 33 and so far so good, thanks so much!! :)

@simbadMarino Can you please elaborate on the full steps you took? I mean which files you modified, etc ? Sorry for being a noob here

@ehsan6sha , sure, below I describe what you need to do:

  1. Download your prefered go version source code from: https://go.dev/dl/ (I downloaded go1.19.9 for instance) and unzip it into your home/yourusername directory, if you already have a "go" folder remove it and later on unzip.image
image
  1. As per #40569 (comment) go to your recently unzipped go folder and find the src folder and proceed to modify src/net/interface_linux.go & /src/syscall/netlink_linux.go files as described in the comment above.
  2. Open a terminal window and go to your /home/yourusername/go/src, then build your modified go source by using sending $ ./all.bash through the command line as per go source install instructions
  3. After successful build, go to your golfing project and build your binaries as usual, change your targetSdkVersion to 30 or above in your build.gradle file, compile your project in Android Studio et voila!

As @yan12125 this is a super clean patch in the go source code to work around the issue, looks effective until now :). Next step its to upload my app to google play as final test hehe

mpl commented

I was able to work around this issue by calling getifaddrs via cgo

Brilliant! Inspired by your work, I looked into how Android implements getifaddrs, and found that RTM_GETADDR is used for non-system apps as RTM_GETLINK is forbidden [1]. On the other hand, there are already some code using RTM_GETADDR in Go [2]. I collected relevant functions, removed usage of forbidden RTM_GETLINK and got a working version:

Pure-Go InterfaceAddrs() for newer Android

import (
	"net"
	"os"
	"syscall"
	"unsafe"
)

// NetlinkRouteRequest represents a request message to receive routing
// and link states from the kernel.
type NetlinkRouteRequest struct {
	Header syscall.NlMsghdr
	Data   syscall.RtGenmsg
}

func (rr *NetlinkRouteRequest) toWireFormat() []byte {
	b := make([]byte, rr.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid
	b[16] = byte(rr.Data.Family)
	return b
}

func newNetlinkRouteRequest(proto, seq, family int) []byte {
	rr := &NetlinkRouteRequest{}
	rr.Header.Len = uint32(syscall.NLMSG_HDRLEN + syscall.SizeofRtGenmsg)
	rr.Header.Type = uint16(proto)
	rr.Header.Flags = syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST
	rr.Header.Seq = uint32(seq)
	rr.Data.Family = uint8(family)
	return rr.toWireFormat()
}

// NetlinkRIB returns routing information base, as known as RIB, which
// consists of network facility information, states and parameters.
func NetlinkRIB(proto, family int) ([]byte, error) {
	s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, syscall.NETLINK_ROUTE)
	if err != nil {
		return nil, err
	}
	defer syscall.Close(s)
	sa := &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
	wb := newNetlinkRouteRequest(proto, 1, family)
	if err := syscall.Sendto(s, wb, 0, sa); err != nil {
		return nil, err
	}
	lsa, err := syscall.Getsockname(s)
	if err != nil {
		return nil, err
	}
	lsanl, ok := lsa.(*syscall.SockaddrNetlink)
	if !ok {
		return nil, syscall.EINVAL
	}
	var tab []byte
	rbNew := make([]byte, syscall.Getpagesize())
done:
	for {
		rb := rbNew
		nr, _, err := syscall.Recvfrom(s, rb, 0)
		if err != nil {
			return nil, err
		}
		if nr < syscall.NLMSG_HDRLEN {
			return nil, syscall.EINVAL
		}
		rb = rb[:nr]
		tab = append(tab, rb...)
		msgs, err := syscall.ParseNetlinkMessage(rb)
		if err != nil {
			return nil, err
		}
		for _, m := range msgs {
			if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid {
				return nil, syscall.EINVAL
			}
			if m.Header.Type == syscall.NLMSG_DONE {
				break done
			}
			if m.Header.Type == syscall.NLMSG_ERROR {
				return nil, syscall.EINVAL
			}
		}
	}
	return tab, nil
}


func newAddr(ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) net.Addr {
	var ipPointToPoint bool
	// Seems like we need to make sure whether the IP interface
	// stack consists of IP point-to-point numbered or unnumbered
	// addressing.
	for _, a := range attrs {
		if a.Attr.Type == syscall.IFA_LOCAL {
			ipPointToPoint = true
			break
		}
	}
	for _, a := range attrs {
		if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS {
			continue
		}
		switch ifam.Family {
		case syscall.AF_INET:
			return &net.IPNet{IP: net.IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv4len)}
		case syscall.AF_INET6:
			ifa := &net.IPNet{IP: make(net.IP, net.IPv6len), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv6len)}
			copy(ifa.IP, a.Value[:])
			return ifa
		}
	}
	return nil
}

func addrTable(msgs []syscall.NetlinkMessage) ([]net.Addr, error) {
	var ifat []net.Addr
loop:
	for _, m := range msgs {
		switch m.Header.Type {
		case syscall.NLMSG_DONE:
			break loop
		case syscall.RTM_NEWADDR:
			ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
			attrs, err := syscall.ParseNetlinkRouteAttr(&m)
			if err != nil {
				return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
			}
			ifa := newAddr(ifam, attrs)
			if ifa != nil {
				ifat = append(ifat, ifa)
			}
		}
	}
	return ifat, nil
}

func interfaceAddrTable() ([]net.Addr, error) {
	tab, err := NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
	if err != nil {
		return nil, os.NewSyscallError("netlinkrib", err)
	}
	msgs, err := syscall.ParseNetlinkMessage(tab)
	if err != nil {
		return nil, os.NewSyscallError("parsenetlinkmessage", err)
	}
	ifat, err := addrTable(msgs)
	if err != nil {
		return nil, err
	}
	return ifat, nil
}

func InterfaceAddrs() ([]net.Addr, error) {
	ifat, err := interfaceAddrTable()
	if err != nil {
		err = &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
	}
	return ifat, err
}

[1] https://android.googlesource.com/platform/bionic/+/refs/tags/android-13.0.0_r18/libc/bionic/ifaddrs.cpp#315 [2] https://github.com/golang/go/blob/go1.20rc1/src/net/interface_linux.go#L124

Hello @yan12125 ,

Do you think an approach similar to what you did above could be achieved to also "fix" the net.Interfaces() and net.InterfaceByName() calls on android?

Thanks

Do you think an approach similar to what you did above could be achieved to also "fix" the net.Interfaces() and net.InterfaceByName() calls on android?

Probably not. Apparently both functions need RTM_GETLINK, which is forbidden on newer Android.

mpl commented

Do you think an approach similar to what you did above could be achieved to also "fix" the net.Interfaces() and net.InterfaceByName() calls on android?

Probably not. Apparently both functions need RTM_GETLINK, which is forbidden on newer Android.

ah, that's unfortunate, thanks.
I suppose that's why tailscale went the way they did, relying on java to do the calls for them.

I found a way to solve net.Interface() and net.InterfaceAddrs() problems, you can check the repository: https://github.com/wlynxg/anet.
Hope this helps everyone to fix this bug

@simbadMarino Followed your approach, tried everything. Still getting the error "route ip+net: netlinkrib: permission denied". Building library using 'gomobile bind -target android ...'. Did you use 'gomobile build' to build .apk for btfs ?

@wlynxg Tried applying your fix, tests are failing due to "imports os: import cycle not allowed"

--- FAIL: TestVet (0.00s) --- FAIL: TestVet/asm (0.07s) vet_test.go:145: error check failed: asm1.s:9: missing error "\\[amd64\\] arg1: invalid MOVW of x\\+0\\(FP\\); int8 is 1-byte value" asm1.s:23: missing error "\\[amd64\\] cpx: invalid MOVO of x\\+0\\(FP\\); complex64 is 8-byte value containing x_real\\+0\\(FP\\) and x_imag\\+4\\(FP\\)" asm1.s:24: missing error "\\[amd64\\] cpx: invalid MOVSD of y\\+8\\(FP\\); complex128 is 16-byte value containing y_real\\+8\\(FP\\) and y_imag\\+16\\(FP\\)" Unmatched Errors: package testmain imports os imports internal/poll imports internal/syscall/unix imports syscall imports os: import cycle not allowed

@simbadMarino Could you please share what were your env variables while building modified go source and project binaries ?

@wlynxg Tried applying your fix, tests are failing due to "imports os: import cycle not allowed"

--- FAIL: TestVet (0.00s) --- FAIL: TestVet/asm (0.07s) vet_test.go:145: error check failed: asm1.s:9: missing error "\\[amd64\\] arg1: invalid MOVW of x\\+0\\(FP\\); int8 is 1-byte value" asm1.s:23: missing error "\\[amd64\\] cpx: invalid MOVO of x\\+0\\(FP\\); complex64 is 8-byte value containing x_real\\+0\\(FP\\) and x_imag\\+4\\(FP\\)" asm1.s:24: missing error "\\[amd64\\] cpx: invalid MOVSD of y\\+8\\(FP\\); complex128 is 16-byte value containing y_real\\+8\\(FP\\) and y_imag\\+16\\(FP\\)" Unmatched Errors: package testmain imports os imports internal/poll imports internal/syscall/unix imports syscall imports os: import cycle not allowed

I am sorry for such a problem, I will re-do the PR later

@simbadMarino Followed your approach, tried everything. Still getting the error "route ip+net: netlinkrib: permission denied". Building library using 'gomobile bind -target android ...'. Did you use 'gomobile build' to build .apk for btfs ?

Hello mate, I'm not using gomobile, I used the c-shared approach, not sure if this could fit your use case but what I need is to create a binary file to use it as part of an Android app as a native library. If this is your case you can try my approach checking my MakeFile: https://github.com/simbadMarino/btfs-sharedLib/blob/main/cmd/btfs/Makefile

If you are using pure go to build your Android app then I'm not sure what else could you do but in theory a c-shared library calls gomobile at some point in the background, so in theory the patch from @wlynxg should work for you as well...

My guess is that your system is still using an unmodified go to compile instead of your modified golang install. Try completely removing go and re-do the building from source again if you havent' tried that already.

Good luck!

As I understand it we are all waiting for these comments to be corrected in PR https://go-review.googlesource.com/c/go/+/507415?tab=comments#message-1fdf96411b7e257df1fb910bc6774a32c569359a

If anyone else can - help fix them.

As I understand it we are all waiting for these comments to be corrected in PR https://go-review.googlesource.com/c/go/+/507415?tab=comments#message-1fdf96411b7e257df1fb910bc6774a32c569359a

If anyone else can - help fix them.

I am looking for a better way to obtain the version number of the Android system for better problem-solving

I tried to obtain the Android version number from the system, but I found that it is very difficult to obtain the Android system version number. Different versions of Android have different underlying implementations of obtaining the version number, and they also need to interact with the Android system (the version number is read in the shared in memory)

bt90 commented
/system/bin/getprop ro.build.version.release
/system/bin/getprop ro.build.version.release

It may not be feasible to introduce an external command in the standard library that may change😒.

bt90 commented

Do you want to use the version to determine if the fallback should be used?

Do you want to use the version to determine if the fallback should be used?

yeah

bt90 commented

The target API level 30 has been mandatory for GooglePlay store apps for quite some time. Apart from rooted phones and thirdparty stores, the restriction is already the default?

The target API level 30 has been mandatory for GooglePlay store apps for quite some time. Apart from rooted phones and thirdparty stores, the restriction is already the default?

To be precise, this has nothing to do with the SDK version number, but with the Android system. https://developer.android.com/training/articles/user-data-ids#mac-11-plus

bt90 commented

My point is that it's better to use the simple fix and restore basic functionality for 75% of the devices out there, and accept that information like the MAC will be dropped for the shrinking number of phones running Android 10 or older.

@wlynxg Tried applying your fix, tests are failing due to "imports os: import cycle not allowed"
--- FAIL: TestVet (0.00s) --- FAIL: TestVet/asm (0.07s) vet_test.go:145: error check failed: asm1.s:9: missing error "\\[amd64\\] arg1: invalid MOVW of x\\+0\\(FP\\); int8 is 1-byte value" asm1.s:23: missing error "\\[amd64\\] cpx: invalid MOVO of x\\+0\\(FP\\); complex64 is 8-byte value containing x_real\\+0\\(FP\\) and x_imag\\+4\\(FP\\)" asm1.s:24: missing error "\\[amd64\\] cpx: invalid MOVSD of y\\+8\\(FP\\); complex128 is 16-byte value containing y_real\\+8\\(FP\\) and y_imag\\+16\\(FP\\)" Unmatched Errors: package testmain imports os imports internal/poll imports internal/syscall/unix imports syscall imports os: import cycle not allowed

I am sorry for such a problem, I will re-do the PR later

@wlynxg Hello there, I'm having similar issues like this and cannot compile the modified go src when adding your android target files. Is there something we need to modify after copying and pasting into the src folder? thanks!

@wlynxg Tried applying your fix, tests are failing due to "imports os: import cycle not allowed"
--- FAIL: TestVet (0.00s) --- FAIL: TestVet/asm (0.07s) vet_test.go:145: error check failed: asm1.s:9: missing error "\\[amd64\\] arg1: invalid MOVW of x\\+0\\(FP\\); int8 is 1-byte value" asm1.s:23: missing error "\\[amd64\\] cpx: invalid MOVO of x\\+0\\(FP\\); complex64 is 8-byte value containing x_real\\+0\\(FP\\) and x_imag\\+4\\(FP\\)" asm1.s:24: missing error "\\[amd64\\] cpx: invalid MOVSD of y\\+8\\(FP\\); complex128 is 16-byte value containing y_real\\+8\\(FP\\) and y_imag\\+16\\(FP\\)" Unmatched Errors: package testmain imports os imports internal/poll imports internal/syscall/unix imports syscall imports os: import cycle not allowed

I am sorry for such a problem, I will re-do the PR later

@wlynxg Hello there, I'm having similar issues like this and cannot compile the modified go src when adding your android target files. Is there something we need to modify after copying and pasting into the src folder? thanks!

Sorry, since this is my first PR project, I did not perform compilation testing, which resulted in such a problem. I re-investigated the problem and found that the compilation failed due to package references. I re-submitted the code.

Marked my comment as resolved because it was relevant to that import cycle !

I'm running into the same problem while attempting to utilize a self-made GOLANG module in an Android app. This module joins a particular room on a LiveKit server (WebRTC SFU) and publishes up to three video streams. While this module perfectly works embedded in Python, I'm seeing kind of these traces when running under Android:

I/GoLog: "msg"="could not create answer" "error"="failed to create network: route ip+net: netlinkrib: permission denied"
I/GoLog: "msg"="could not set remote description" "error"="failed to create network: route ip+net: netlinkrib: permission denied"
I/GoLog: "msg"="could not add ICE candidate" "error"="failed to create network: route ip+net: netlinkrib: permission denied"

Those messages are generated by the LiveKit server GO SDK, but the root of the problem seems to be in what has been discussed here. As far as I could follow the discussion, there seems to be a fix already. I'm not too familiar with GOLANG up to now. My go.mod contains these entries w.r.t. gomobile:

	golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
	golang.org/x/mod v0.14.0 // indirect

If there is a patch for the problem what would I have to do to apply that?

TIA

PS: I can confirm that I only see this problem on my Android 13 device. The older Android 8 device doesn't have that issue.

PS: I can confirm that I only see this problem on my Android 13 device. The older Android 8 device doesn't have that issue.

Yes, this problem exists since Android 11, if you need help, you can contact me.

@wlynxg Hi, I've seen this PR hanging for a long time, is it has any progress now? Thanks!

@wlynxg Hi, I've seen this PR hanging for a long time, is it has any progress now? Thanks!

The current main problem is that you cannot get the Android version number directly through go. However, you can tell go the Android version number by calling the go function on Android. There are no other problems for the time being.

@wlynxg Hi, I've seen this PR hanging for a long time, is it has any progress now? Thanks!

The current main problem is that you cannot get the Android version number directly through go. However, you can tell go the Android version number by calling the go function on Android. There are no other problems for the time being.

Is the background that you want to use the Andoird version instead of os.IsPermission as a fallback?

instead of os.IsPermission as a fallback?

yeah

@wlynxg
Alright, if this is just to solve the cyclic dependency problem , can we use the oserror.ErrPermission assertion instead?

@wlynxg Alright, if this is just to solve the cyclic dependency problem , can we use the oserror.ErrPermission assertion instead?

I fixed this problem in a later update and used oserror.ErrPermission

@wlynxg Alright, if this is just to solve the cyclic dependency problem , can we use the assertion instead?oserror.ErrPermission

I fixed this problem in a later update and used oserror.ErrPermission

Sorry, my mistake. So what exactly is the problem? It looks like it can be merged now from my perspective.

Yes, this problem exists since Android 11, if you need help, you can contact me.

Hi @wlynxg, thanks for the offer. Well, I see the changes linked above, but I wouldn't know how to apply them. I generally know how to apply a PR, but in this scenario I'm just a GOMOBILE user under Android, and my GOLANG is maybe three days old or so :) I basically don't know, where to apply the change.

Yes, this problem exists since Android 11, if you need help, you can contact me.

Hi @wlynxg, thanks for the offer. Well, I see the changes linked above, but I wouldn't know how to apply them. I generally know how to apply a PR, but in this scenario I'm just a GOMOBILE user under Android, and my GOLANG is maybe three days old or so :) I basically don't know, where to apply the change.

The official has not accepted this change for the time being. My suggestion is to temporarily use a third-party library on the Android platform instead of the net package to obtain network card information. You can refer to my code: https://github.com/wlynxg/anet.

@wlynxg Thanks, did that right now. My problem is, I'm using net.Listen for a Unix socket. This is not part of your fork, seemingly.

EDIT: But even if you support that, I'm sure it is not my code which provokes that problem. It is the LiveKit SDKs problem, which most likely also uses net. I'm sure I can't convince them to use anet.

@wlynxg Thanks, did that right now. My problem is, I'm using net.Listen for a Unix socket. This is not part of your fork, seemingly.

If you can provide relevant code, I can help you analyze it.

Well, yes. I'm just calling net once in my code with socket, err := net.Listen(socketType, address). This can't currently be replaced by anet, because missing. But as said in my post editing above: This is not my problem, it is a problem provoked by valid net calls from the LiveKit GOLANG SDK and so the problem needs to be solved in net most likely.

I'm having a question: This problem is open since 2020. There is no workaround. Basically it means, that all Android apps, relying on GO in some way and "doing network things", will not work if they are running Android 11 or higher (at least the latter is for sure not that exotic anymore as it was in 2020)

I'm wondering why this issue is not making bigger waves, but most likely the relevance of gomobile for Android is lower than expected (which is kind of a pity, since it is in some limited ways a perfect replacement of C++).

But at least network things should be working.

Same suffering here. 4 years old bug, unbelievable!

Findings, while using same gomobile arr in Android App,

  • If App built with targetSdkVersion 29, it can run flawlessly on Android 11 device, but fail on Android 13 device.
  • If App built with targetSdkVersion 30 and above, it fails on all devices, with err msg "netlinkrib: permission denied"

Is there any workaround available, even only with targetSdkVersion 29?

@invented-pro At least for me it is solved. Apply this PR to a source installation of GOLANG #61089

I'll take a try. Thanks @neilyoung

bt90 commented

@wlynxg what's the current state of the PR?

Merging is blocked for some reasons.

I'll take a try. Thanks @neilyoung

image

src + PR build on macos failed due to some tests.
which platfrom did you manage? @neilyoung

@invented-pro I'm on macOS 14.2.1, M1

@invented-pro I'm on macOS 14.2.1, M1

cannot build on 14.2.1 M2

I had a recent binary installation on disk, then followed this gist https://go.dev/doc/install/source. Applied the patch manually. Made the patched GOLANG default then.

EDIT: Aarggh.... Realized right now, that I did it on Ubuntu 20.04. Sorry for this...

I had a recent binary installation on disk, then followed this gist https://go.dev/doc/install/source. Applied the patch manually. Made the patched GOLANG default then.

EDIT: Aarggh.... Realized right now, that I did it on Ubuntu 20.04. Sorry for this...

Thanks. I'll try on linux.

I built golang from src patched with PR61089, then used it to build aar file, finally it works as expected now on Android11 & 13, no "netlinkrib: permission denied" anymore.

Sincerely thank @wlynxg for the PR !!! Thank @neilyoung for guidance!!!

Hints to whom it may concern:

  • Golang built from src failed on my Mac M2; it's done on a Debian11 host with no error.
  • If you see error "zipdata redeclared in this block" during build, that's only because you build src multi times, time/tzdata/zzipdata.go is generated automatically, just delete it.
  • For linux env, don't forget to config Android SDK, NDK, etc.
  • GOROOT is your new go bin directory, don't point GOPATH to this GOROOT.
  • New aar file from Go 1.21 makes my App crash on Android frequently, while Go 1.20 seams ok.