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

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 ®:

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

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 32
  • Comments: 89 (3 by maintainers)

Commits related to this issue

Most upvoted comments

Any updates?

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.

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

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, 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

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 https://github.com/tailscale/tailscale-android/commit/fd42b4b3526a33338e6fa3f38ce332380139a860, 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.

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

@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

  2. As per https://github.com/golang/go/issues/40569#issuecomment-1600898227 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.

  3. 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

  4. 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

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 https://github.com/golang/go/issues/40569#issuecomment-1191823859

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

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.

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

@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.

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.

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

@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

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.

@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

@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 {

any update?

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

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.

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