Files
SunnyNet/JavaApi/wifiIP.go
2025-12-03 22:00:52 +08:00

179 lines
5.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build android
// +build android
package JavaJni
import (
"net" // 解析 IP 字符串为 net.IP
"runtime" // 锁定线程,保证 JNI 在线程内一致
)
// GetWifiAddr 通过 JNI 反射调用 java.net.NetworkInterface 枚举所有内网 IP
// 不依赖 android.permission.ACCESS_WIFI_STATE
func GetWifiAddr() (ips []net.IP) {
// JNI 要求同一个 JNIEnv 只能在创建它的线程里使用,所以先锁线程
runtime.LockOSThread()
defer runtime.UnlockOSThread()
// 如果虚拟机还没初始化,直接返回
if GlobalVM == 0 {
return
}
// 把当前线程 attach 到 JavaVM拿到 EnvJNIEnv 封装)
env, ret := GlobalVM.AttachCurrentThread()
if ret != JNI_OK {
// attach 失败也直接返回
return
}
// 函数结束时 detach 当前线程
defer GlobalVM.DetachCurrentThread()
// 找到 java.net.NetworkInterface 类
niClass := env.FindClass("java.net.NetworkInterface")
if niClass == 0 {
// 找不到类直接返回
return
}
// 获取静态方法 NetworkInterface.getNetworkInterfaces()Ljava/util/Enumeration;
getNIMethod := env.GetStaticMethodID(niClass, "getNetworkInterfaces", "()Ljava/util/Enumeration;")
if getNIMethod == 0 {
// 方法 ID 获取失败
return
}
// 调用 NetworkInterface.getNetworkInterfaces(),返回一个 Enumeration
enumObj := env.CallStaticObjectMethodA(niClass, getNIMethod)
if enumObj == 0 {
// 返回为 null
return
}
// 用完枚举对象后释放本地引用
defer env.DeleteLocalRef(enumObj)
// 拿到 Enumeration 的 Class用来找 hasMoreElements / nextElement
enumClass := env.GetObjectClass(enumObj)
if enumClass == 0 {
return
}
// Enumeration.hasMoreElements()Z
hasMoreMethod := env.GetMethodID(enumClass, "hasMoreElements", "()Z")
// Enumeration.nextElement()Ljava/lang/Object;
nextElementMethod := env.GetMethodID(enumClass, "nextElement", "()Ljava/lang/Object;")
if hasMoreMethod == 0 || nextElementMethod == 0 {
return
}
// NetworkInterface.getInetAddresses()Ljava/util/Enumeration;
getInetAddressesMethod := env.GetMethodID(niClass, "getInetAddresses", "()Ljava/util/Enumeration;")
if getInetAddressesMethod == 0 {
return
}
// InetAddress 相关方法isSiteLocalAddress / isLoopbackAddress / getHostAddress
inetClass := env.FindClass("java.net.InetAddress")
if inetClass == 0 {
// 理论上不会失败,防御性判断
return
}
isSiteLocalMethod := env.GetMethodID(inetClass, "isSiteLocalAddress", "()Z") // 判断是否内网地址
isLoopbackMethod := env.GetMethodID(inetClass, "isLoopbackAddress", "()Z") // 判断是否回环地址
getHostAddressMethod := env.GetMethodID(inetClass, "getHostAddress", "()Ljava/lang/String;") // 获取 IP 字符串
if isSiteLocalMethod == 0 || isLoopbackMethod == 0 || getHostAddressMethod == 0 {
return
}
// 外层循环:遍历所有 NetworkInterface
for {
// 调用 Enumeration.hasMoreElements()
hasMore := env.CallBooleanMethodA(enumObj, hasMoreMethod)
if !hasMore {
// 没有更多元素,结束外层循环
break
}
// 调用 Enumeration.nextElement(),得到一个 NetworkInterface 对象
niObj := env.CallObjectMethodA(enumObj, nextElementMethod)
if niObj == 0 {
// 理论上不该为 null防御性判断
continue
}
// 调用 NetworkInterface.getInetAddresses(),拿到一个 InetAddress 的 Enumeration
addrEnumObj := env.CallObjectMethodA(niObj, getInetAddressesMethod)
// NetworkInterface 对象用完,释放本地引用
env.DeleteLocalRef(niObj)
if addrEnumObj == 0 {
// 该网卡没有地址,跳过
continue
}
// 拿到 InetAddress 枚举的 Class
addrEnumClass := env.GetObjectClass(addrEnumObj)
if addrEnumClass == 0 {
env.DeleteLocalRef(addrEnumObj)
continue
}
// InetAddress 枚举同样用 hasMoreElements / nextElement 迭代
addrHasMoreMethod := env.GetMethodID(addrEnumClass, "hasMoreElements", "()Z")
addrNextElementMethod := env.GetMethodID(addrEnumClass, "nextElement", "()Ljava/lang/Object;")
if addrHasMoreMethod == 0 || addrNextElementMethod == 0 {
env.DeleteLocalRef(addrEnumObj)
continue
}
// 内层循环:遍历当前网卡上的所有 InetAddress
for {
// hasMoreElements()
addrHasMore := env.CallBooleanMethodA(addrEnumObj, addrHasMoreMethod)
if !addrHasMore {
// 当前网卡没有更多 IP退出内层循环
break
}
// nextElement() 得到一个 InetAddress 对象
ipObj := env.CallObjectMethodA(addrEnumObj, addrNextElementMethod)
if ipObj == 0 {
// 防御性判断
continue
}
// 调用 InetAddress.isSiteLocalAddress(),判定是否内网地址
isSiteLocal := env.CallBooleanMethodA(ipObj, isSiteLocalMethod)
// 调用 InetAddress.isLoopbackAddress(),排除 127.0.0.1 这类回环地址
isLoopback := env.CallBooleanMethodA(ipObj, isLoopbackMethod)
if isSiteLocal && !isLoopback {
// 满足site-local 且不是 loopback认为是内网 IP
// 调用 InetAddress.getHostAddress() 拿到 IP 字符串
hostStrObj := env.CallObjectMethodA(ipObj, getHostAddressMethod)
if hostStrObj != 0 {
// 将 jstring 转成 Go string
hostStr := env.GetString(Jstring(hostStrObj))
// 用完 jstring释放引用
env.DeleteLocalRef(hostStrObj)
if hostStr != "" {
// 解析为 net.IP成功则加入结果切片
if parsedIP := net.ParseIP(hostStr); parsedIP != nil {
ips = append(ips, parsedIP)
}
}
}
}
// InetAddress 对象用完,释放本地引用
env.DeleteLocalRef(ipObj)
}
// InetAddress 枚举对象用完,释放本地引用
env.DeleteLocalRef(addrEnumObj)
}
// 把所有收集到的内网 IP 返回
return
}