Android13执行ip neigh show返回退出码1,求热点连接设备IP获取方案
问题背景
我开发了一款简易自定义应用,用于查询连接到手机本地热点的自定义设备的温度、速度等信息。在Android 12及以下系统中,通过runtime.exec('ip neigh show')命令,配合compileSdkVersion 29、buildToolsVersion '29.0.3'、minSdkVersion 21和targetSdkVersion 27的配置,可完美获取连接设备的IP地址。
但在Android 13设备上调试时,执行runtime.exec('ip neigh show')返回ExitCode = 1,推测是Android 13对该命令读取的/proc/net/arp文件施加了严格限制。
补充信息1
目前在Android 13设备上仅能通过以下代码获取热点IP:
wifiManager = (WifiManager) getActivity().getApplicationContext().getSystemService(Context.WIFI_SERVICE); wifiInfo = wifiManager.getConnectionInfo(); ipAddress = Formatter.formatIpAddress( wifiInfo.getIpAddress() );
能否通过该IP查询连接设备的IP,而无需使用ip neigh show?
补充信息2
尝试使用以下代码检查CONNECTIVITY_MANAGER:
Network[] allNetworks = connectivityManager.getAllNetworks(); for (Network network: allNetworks) { NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network); LinkProperties lp = connectivityManager.getLinkProperties(network); String id = lp.getInterfaceName(); Log.d("IdInfo", "id: " + id); }
调试中仅看到MOBILE[LTE]连接,热点是否应被视为连接主机?
解决方案
1. 使用WifiManager的getDhcpLeases()方法(Android 13+)
Android 13(API 33)新增了WifiManager.getDhcpLeases()方法,可直接获取连接到热点的设备的DHCP租赁信息,其中包含设备IP、MAC地址等关键数据。
示例代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); List<DhcpLease> leases = wifiManager.getDhcpLeases(); for (DhcpLease lease : leases) { String deviceIp = lease.getIpAddress().getHostAddress(); String deviceMac = lease.getHardwareAddress(); // 处理设备信息 } }
注意:需要在Manifest中声明ACCESS_WIFI_STATE权限,Android 13+中该权限为普通权限,无需动态申请。
2. 遍历热点子网IP进行ping探测(兼容方案)
如果需要兼容更低版本,或者无法使用getDhcpLeases(),可以通过热点IP推算子网范围,然后逐个ping探测在线设备:
- 先通过已有代码获取热点IP(Android热点默认网关IP通常为
192.168.43.1) - 子网范围一般是
192.168.43.2到192.168.43.254 - 使用多线程对每个IP执行ping操作,判断设备是否在线
示例代码片段:
String hotspotIp = Formatter.formatIpAddress(wifiInfo.getIpAddress()); // 拆分IP,生成子网前缀 String[] parts = hotspotIp.split("\\."); String subnetPrefix = parts[0] + "." + parts[1] + "." + parts[2] + "."; // 遍历子网内的IP for (int i = 2; i <= 254; i++) { String targetIp = subnetPrefix + i; new Thread(() -> { try { Process process = Runtime.getRuntime().exec("ping -c 1 -W 1 " + targetIp); int exitCode = process.waitFor(); if (exitCode == 0) { // 该IP设备在线,记录信息 Log.d("OnlineDevice", "IP: " + targetIp); } } catch (IOException | InterruptedException e) { e.printStackTrace(); } }).start(); }
注意:这种方法效率较低,需要INTERNET权限,且部分设备可能禁用ping响应导致漏检。
3. 关于CONNECTIVITY_MANAGER的问题
热点作为AP模式时,属于本地网络,不会被getAllNetworks()标记为"连接"状态(设备本身是热点提供者而非连接者)。可以通过LinkProperties的getInterfaceName()识别热点接口,通常热点接口名为wlan0或ap0,结合WifiManager.isWifiApEnabled()判断热点状态后筛选对应接口。
示例代码:
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); if (wifiManager.isWifiApEnabled()) { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); Network[] allNetworks = connectivityManager.getAllNetworks(); for (Network network : allNetworks) { LinkProperties lp = connectivityManager.getLinkProperties(network); if (lp != null) { String ifName = lp.getInterfaceName(); // 热点接口通常包含"ap"或"wlan"关键词 if (ifName.contains("ap") || ifName.contains("wlan")) { Log.d("HotspotInterface", "Interface: " + ifName); // 获取该接口的IP信息 List<InetAddress> addresses = lp.getAddresses(); for (InetAddress addr : addresses) { Log.d("HotspotIP", "IP: " + addr.getHostAddress()); } } } } }
内容的提问来源于stack exchange,提问作者Luigino

