文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android静态ip设置的坑

2023-08-30 17:35

关注

Android静态ip设置的坑

Android静态ip设置,对于这个功能,如果没有接触过,会给人感觉是个特别简单的功能,直接调用系统的接口即可,其实这个功能还是有许多坑的,因为谷歌在Android SDK中对相关的API进行非系统层的隐藏,打上了@hide标签,使得上层无法直接调用现有的接口,其中隐藏的API包含:EthernetManager,IpConfiguration,StaticIpConfiguration等接口,其中有的API虽然提供,但是关键的接口却被隐藏了。导致本来简单功能,变得无比繁琐,因此本文就对静态ip设置的坑做个总结,为相关开发者提供一个简易的参考。
解决Android静态ip设置的问题,可以从两个方面着手:

一方面有系统层接口的支持

系统接口也就是直接由系统层提供接口,一般是framework jar包上层直接调用接口传参,但是需要调整Gradle中的SDK的加载顺序,保证framework包正确替换AndroidSDK的jar包,否则编译会报错。

另一方面就是App层自己使用java的动态机制

动态机制即java的反射,通过反射调用系统隐藏的接口,但是这种方式发现在Android6以下的版本是可以的,Android6以上的版本虽然设置成功,设置里面参数也改了,但是重启你会发现,已经设置的静态模式会自动切换为动态模式,即系统不做数据存储保存,只是零时保存在内存中,所以你重启以后就被系统还原了,参考GitHub分析是高版本谷歌拦截了反射机制,不让非系统层修改数据,而且这个拦截是在C++层进行的,特别恶心,你绕都绕不过去,不过如果你的应用是Launcher应用的话,可以在App层做取巧,就是记录用户重启前是否做静态ip的保存,如果用户操作了静态ip保存,那么App启动的时候在Application的onCreate方法中下发静态ip配置,保证重启前静态ip状态和重启后的状态一致就能解决这个问题。以下就静态ip设置从系统层,App层两个方面做简要的代码实现分析:

1.系统层的实现:就是系统提供的接口直接调用,思路是先创建EthernetManager对象,接着创建StaticIpConfiguration对象,再使用StaticIpConfiguration对象创建IpConfiguration对象,最后将IpConfiguration设置给EthernetManager中即可,代码如下:
public class FEthernetUtil {    private static EthernetManager mEthManager;    private static StaticIpConfiguration mStaticIpConfiguration;    private static IpConfiguration mIpConfiguration;        @SuppressLint("WrongConstant")    public static int setStaticIp(Context context, String iFace, String ip, String gateway, String netmask, String dns1, String dns2) {//获取EthernetManager对象        mEthManager = (EthernetManager) context.getSystemService("ethernet");        if (setStaticIpConfiguration(ip, gateway, netmask, dns1, dns2)) {            mEthManager.setConfiguration(iFace, mIpConfiguration);            return 0;        }        return 1;    }       private static boolean setStaticIpConfiguration(String ip, String gateway, String netmask, String dns1, String dns2) {        Inet4Address inetAddr = (Inet4Address) InetAddresses.parseNumericAddress(ip);        int prefixLength = maskStr2InetMask(netmask);        InetAddress gatewayAddr = InetAddresses.parseNumericAddress(gateway);        InetAddress dnsAddr = InetAddresses.parseNumericAddress(dns1);        if (null == inetAddr || inetAddr.getAddress().toString().isEmpty() || prefixLength == 0 || gatewayAddr.toString().isEmpty() || dnsAddr.toString().isEmpty()) {            return false;        }        String dnsStr2 = dns2;        ArrayList dnsAddrs = new ArrayList();        dnsAddrs.add(dnsAddr);        if (!dnsStr2.isEmpty()) {            dnsAddrs.add(InetAddresses.parseNumericAddress(dns2));        }        mStaticIpConfiguration = new StaticIpConfiguration.Builder().setIpAddress(new LinkAddress(inetAddr, prefixLength)).setGateway(gatewayAddr).setDnsServers(dnsAddrs).build();        mIpConfiguration = new IpConfiguration();        mIpConfiguration.setIpAssignment(IpConfiguration.IpAssignment.STATIC);        mIpConfiguration.setProxySettings(IpConfiguration.ProxySettings.NONE);        mIpConfiguration.setStaticIpConfiguration(mStaticIpConfiguration);        return true;    }         private static int maskStr2InetMask(String maskStr) {        StringBuffer sb;        String str;        int inetmask = 0;        int count = 0;                Pattern pattern = Pattern.compile("(^((\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])$)|^(\\d|[1-2]\\d|3[0-2])$");        if (pattern.matcher(maskStr).matches() == false) {            return 0;        }        String[] ipSegment = maskStr.split("\\.");        for (String s : ipSegment) {            sb = new StringBuffer(Integer.toBinaryString(Integer.parseInt(s)));            str = sb.reverse().toString();            count = 0;            for (int i = 0; i < str.length(); i++) {                i = str.indexOf("1", i);                if (i == -1) break;                count++;            }            inetmask += count;        }        return inetmask;    }}

拿到系统层的jar包,跟使用其他jar一样进行gradle依赖配置,需要注意是gradle中的jar包加载顺序修改:

plugins {    id 'com.android.library'    id 'org.jetbrains.kotlin.android'}   ......dependencies {    implementation 'androidx.core:core-ktx:1.8.0'    api(files("libs/framework.jar"))//系统层配置静态ip的jar包}gradle.projectsEvaluated {    tasks.withType(JavaCompile) {        Set fileSet = options.bootstrapClasspath.getFiles();        List newFileList = new ArrayList<>()        newFileList.add(new File("libs/framework.jar"))//加载本地系统jar包        newFileList.addAll(fileSet)        options.bootstrapClasspath = files(newFileList.toArray())    }}//调整jar包的顺序preBuild {    doLast {        def imlFile = file(project.name + ".iml")        println('Change ' + project.name + '.iml order')        try {            def parsedXml = (new XmlParser()).parse(imlFile)            def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }            parsedXml.component[1].remove(jdkNode)            def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"            new groovy.util.Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])            groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))        } catch (FileNotFoundException e) {        }    }
2.App层的实现:App层实现通过反射来操作,实现思路其实和系统层实现是一样的逻辑,首先需要先获取EthernetManager对象,接着创建StaticIpConfiguration对象,再根据StaticIpConfiguration对象反射创建,IpConfiguration对象,和系统层对比App层是使用反射来获取对象而已逻辑是一样的:以下是代码实现:
class EthernetUtil @Inject constructor(    @ApplicationContext private val context: Context,    private val ipConfigRepository: IPConfigRepository,    private val guideRepository: GuideRepository) {        fun setEthernetStaticIp(        address: String,        mask: String,        gate: String,        dns: String,        dns2: String    ): Boolean {        d(TAG, "setEthernetStaticIp --> ip: $address, mask: $mask, gate: $gate, dns: $dns, dns2: $dns2")        return try {            val ethernetManagerCls = Class.forName("android.net.EthernetManager")            //获取EthernetManager实例            @SuppressLint("WrongConstant") val ethManager = getApp().getSystemService("ethernet")            val ipInfo = ipConfigRepository.getDefaultIp()            val primaryDNS = if (dns.isNullOrEmpty() || dns.isNullOrBlank()) {                ipInfo.IPAddress?.PrimaryDNS?.ipAddress            } else {                dns            }            val secondaryDNS = if (dns2.isNullOrEmpty() || dns2.isNullOrBlank()) {                ipInfo.IPAddress?.SecondaryDNS?.ipAddress            } else {                dns2            }            //创建StaticIpConfiguration            val staticIpConfiguration =                newStaticIpConfiguration(address, gate, mask, primaryDNS ?: "", secondaryDNS ?: "")            //创建IpConfiguration            val ipConfiguration = newIpConfiguration(staticIpConfiguration)            //获取EthernetManager的setConfiguration()            val setConfigurationMethod = ethernetManagerCls.getDeclaredMethod(                "setConfiguration",                *arrayOf(NET_ETH_0.javaClass, ipConfiguration.javaClass)            )            //保存静态ip设置            saveIpSettings(getApp(), address, mask, gate, dns, dns2)//保存和硬终端一致dns为空也配置            //设置静态IP            setConfigurationMethod.invoke(ethManager, NET_ETH_0, ipConfiguration)            guideRepository.guideStatus = true            true        } catch (e: Exception) {            e.printStackTrace()            false        }    }        @Throws(Exception::class)    private fun newStaticIpConfiguration(        address: String,        gate: String,        mask: String,        dns: String,        dns2: String    ): Any {        val staticIpConfigurationCls = Class.forName("android.net.StaticIpConfiguration")        //实例化StaticIpConfiguration        val staticIpConfiguration = staticIpConfigurationCls.newInstance()        val ipAddress = staticIpConfigurationCls.getField("ipAddress")        val gateway = staticIpConfigurationCls.getField("gateway")        val domains = staticIpConfigurationCls.getField("domains")        val dnsServers = staticIpConfigurationCls.getField("dnsServers")        //设置ipAddress        ipAddress[staticIpConfiguration] = newLinkAddress(address, mask)        //设置网关        gateway[staticIpConfiguration] = InetAddress.getByName(gate)        //设置掩码        domains[staticIpConfiguration] = mask        //设置dns1        val dnsList = dnsServers[staticIpConfiguration] as ArrayList        dnsList.add(InetAddress.getByName(dns))        //设置dns2        dnsList.add(InetAddress.getByName(dns2))        return staticIpConfiguration    }        @Throws(Exception::class)    private fun newLinkAddress(address: String, mask: String): Any? {        val linkAddressCls = Class.forName("android.net.LinkAddress")        val linkAddressConstructor = linkAddressCls.getDeclaredConstructor(            InetAddress::class.java,            Int::class.java        )        d(TAG, "子网掩码参数:ip: ${InetAddress.getByName(address)} ${maskStr2InetMask(mask)}")        return linkAddressConstructor.newInstance(            InetAddress.getByName(address),            maskStr2InetMask(mask)        )    }        @Throws(Exception::class)    private fun newIpConfiguration(staticIpConfiguration: Any): Any {        val ipConfigurationCls = Class.forName("android.net.IpConfiguration")        val ipConfiguration = ipConfigurationCls.newInstance()        //设置StaticIpConfiguration        val staticIpConfigurationField = ipConfigurationCls.getField("staticIpConfiguration")        staticIpConfigurationField[ipConfiguration] = staticIpConfiguration        //获取ipAssignment、proxySettings的枚举值        val ipConfigurationEnum = getIpConfigurationEnum(ipConfigurationCls)        //设置ipAssignment        val ipAssignment = ipConfigurationCls.getField("ipAssignment")        ipAssignment[ipConfiguration] = ipConfigurationEnum["IpAssignment.STATIC"]        //设置proxySettings        val proxySettings = ipConfigurationCls.getField("proxySettings")        proxySettings[ipConfiguration] = ipConfigurationEnum["ProxySettings.STATIC"]        return ipConfiguration    }        private fun getIpConfigurationEnum(ipConfigurationCls: Class<*>): Map {        val enumMap: MutableMap = HashMap()        val enumClass = ipConfigurationCls.declaredClasses        for (enumC in enumClass) {            val enumConstants = enumC.enumConstants ?: continue            for (enu in enumConstants) {                enumMap[enumC.simpleName + "." + enu.toString()] = enu            }        }        return enumMap    }        private fun saveIpSettings(        context: Context,        address: String,        mask: String,        gate: String,        dns: String,        dns2: String    ) {        val contentResolver = context.contentResolver        Settings.Global.putString(contentResolver, "ethernet_static_ip", address)        Settings.Global.putString(contentResolver, "ethernet_static_mask", mask)        Settings.Global.putString(contentResolver, "ethernet_static_gateway", gate)        Settings.Global.putString(contentResolver, "ethernet_static_dns1", dns)        Settings.Global.putString(contentResolver, "ethernet_static_dns2", dns2)    }        private fun maskStr2InetMask(maskStr: String): Int {        var sb: StringBuffer        var str: String        var inetmask = 0        var count = 0                val pattern: Pattern =            Pattern.compile("(^((\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])$)|^(\\d|[1-2]\\d|3[0-2])$")        if (pattern.matcher(maskStr).matches() === false) {            d(TAG, "subMask is error")            return 0        }        val ipSegment = maskStr.split("\\.".toRegex()).toTypedArray()        for (n in ipSegment.indices) {            sb = StringBuffer(Integer.toBinaryString(ipSegment[n].toInt()))            str = sb.reverse().toString()            count = 0            var i = 0            while (i < str.length) {                i = str.indexOf("1", i)                if (i == -1) break                count++                i++            }            inetmask += count        }        return inetmask    }

来源地址:https://blog.csdn.net/DZMNLFH/article/details/132568731

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-人工智能
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯