【问题标题】:How to configure a static IP address, netmask, gateway programmatically on Android 3.x or 4.x如何在 Android 3.x 或 4.x 上以编程方式配置静态 IP 地址、网络掩码、网关
【发布时间】:2012-05-03 22:35:59
【问题描述】:

我已签入 Stack Overflow 问题API for configuring static IP addresses in an Android application

它在 Android 2.3 之前有效。但是,在更高的 API 级别上没有运气。例如, 我把设置

android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_USE_STATIC_IP, "1");        
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_IP, "192.168.0.100");
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_NETMASK, "255.255.255.0");
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_DNS1, "192.168.0.254");
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_GATEWAY, "192.168.0.254");

但我回去检查:

Setting --> Wi-Fi --> Long Press Access Point SSID --> Modify Network --> check Show advanced options

IP Settings 字段仍为DHCP,但不是Static

确实,我可以使用android.provider.Settings.System.getString() 来取回我设置的内容。它证明设置保存在某处,但系统只是忽略它。

系统在 Android 3.x 和 4.x 上使用除 android.provider.Settings.System 之外的设置,因为该设置是按接入点 SSID 设置的。我可以像在 Android 2.3 上一样修改一个 SSID 的设置吗?

【问题讨论】:

    标签: android networking settings ipv4


    【解决方案1】:

    我意识到 3.x 或 4.x 上没有针对每个 SSID 设置的 API。于是,我查看了源码,发现每个SSID的配置都存放在android.net.wifi.WifiConfiguration中,android.net.wifi.WifiConfiguration是从android.net.wifi.WifiManager获取的。

    在下面的代码中,IpAssignment 是一个枚举,STAICDHCPNONE。 而linkProperties是对象存储IP地址、网关、DNS等...

    linkAddress 是 IP 地址,其网络掩码为 prefixLength(网络掩码中有多少位 1)。

    mRoutesArrayListRouteInfo 可以表示网关。

    mDnses 是 DNS 的 ArrayListInetAddress

    首先,使用WifiConfigurationSSID获取当前配置

    WifiConfiguration wifiConf = null;
    WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
    WifiInfo connectionInfo = wifiManager.getConnectionInfo();
    List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();        
    for (WifiConfiguration conf : configuredNetworks){
        if (conf.networkId == connectionInfo.getNetworkId()){
            wifiConf = conf;
            break;              
        }
    }
    

    由于IpAssignmentlinkProperties是隐藏的,所以可以通过反射获取对象。

    以下方法可以设置SSID WifiConfiguration上声明的IP地址设置:

        public static void setIpAssignment(String assign , WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException{
            setEnumField(wifiConf, assign, "ipAssignment");     
        }
    
        public static void setIpAddress(InetAddress addr, int prefixLength, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException,
        NoSuchMethodException, ClassNotFoundException, InstantiationException, InvocationTargetException{
            Object linkProperties = getField(wifiConf, "linkProperties");
            if(linkProperties == null)return;
            Class laClass = Class.forName("android.net.LinkAddress");
            Constructor laConstructor = laClass.getConstructor(new Class[]{InetAddress.class, int.class});
            Object linkAddress = laConstructor.newInstance(addr, prefixLength);
    
            ArrayList mLinkAddresses = (ArrayList)getDeclaredField(linkProperties, "mLinkAddresses");
            mLinkAddresses.clear();
            mLinkAddresses.add(linkAddress);        
        }
    
        public static void setGateway(InetAddress gateway, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException, 
        ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException{
            Object linkProperties = getField(wifiConf, "linkProperties");
            if(linkProperties == null)return;
            Class routeInfoClass = Class.forName("android.net.RouteInfo");
            Constructor routeInfoConstructor = routeInfoClass.getConstructor(new Class[]{InetAddress.class});
            Object routeInfo = routeInfoConstructor.newInstance(gateway);
    
            ArrayList mRoutes = (ArrayList)getDeclaredField(linkProperties, "mRoutes");
            mRoutes.clear();
            mRoutes.add(routeInfo);
        }
    
        public static void setDNS(InetAddress dns, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException{
            Object linkProperties = getField(wifiConf, "linkProperties");
            if(linkProperties == null)return;
    
            ArrayList<InetAddress> mDnses = (ArrayList<InetAddress>)getDeclaredField(linkProperties, "mDnses");
            mDnses.clear(); //or add a new dns address , here I just want to replace DNS1
            mDnses.add(dns); 
        }
    
        public static Object getField(Object obj, String name)
        throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
            Field f = obj.getClass().getField(name);
            Object out = f.get(obj);
            return out;
        }
    
        public static Object getDeclaredField(Object obj, String name)
        throws SecurityException, NoSuchFieldException,
        IllegalArgumentException, IllegalAccessException {
            Field f = obj.getClass().getDeclaredField(name);
            f.setAccessible(true);
            Object out = f.get(obj);
            return out;
        }  
    
        private static void setEnumField(Object obj, String value, String name)
        throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
            Field f = obj.getClass().getField(name);
            f.set(obj, Enum.valueOf((Class<Enum>) f.getType(), value));
        }
    

    之后,我可以为这个 SSID 设置和更新WifiConfiguration

        try{
            setIpAssignment("STATIC", wifiConf); //or "DHCP" for dynamic setting
            setIpAddress(InetAddress.getByName("192.168.0.100"), 24, wifiConf);
            setGateway(InetAddress.getByName("4.4.4.4"), wifiConf);
            setDNS(InetAddress.getByName("4.4.4.4"), wifiConf);
            wifiManager.updateNetwork(wifiConf); //apply the setting
                wifiManager.saveConfiguration(); //Save it
        }catch(Exception e){
            e.printStackTrace();
        }
    

    编辑: 抱歉,我不检查具有与 Android 4.x 类似 UI 的 Android 3.x 设备。 在 Android 3.x 中,网关存储在 mGatewayslinkProperties 中。 mGatewaysArraylist 类型的 InetAddress。因此,以下应该适用于 Android 3.x。

    public static void setGateway(InetAddress gateway, WifiConfiguration wifiConf)
            throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException, 
            ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException{
                Object linkProperties = getField(wifiConf, "linkProperties");
                if(linkProperties == null)return;
                ArrayList mGateways = (ArrayList)getDeclaredField(linkProperties, "mGateways");
                mGateways.clear();
                mGateways.add(gateway);
            }
    

    Edit2:方法setIpAddresssetGatewaysetDNS应输入为InetAddress类型。

    【讨论】:

    • 嗨,我正在使用此代码,但在 `Class routeInfoClass = Class.forName("android.net.RouteInfo");` 语句中出现错误。我该如何解决。它说java.lang.ClassNotFoundException: android.net.RouteInfo。提前致谢。
    • 我在 3.x 中遇到错误,当我将项目更改为 4.x 并在 ICS 设备中进行测试时,它可以工作。如何在 3.x 中解决此问题?
    • 真的有效吗?我试过 3.x 甚至 4.x 版本,没有显示效果。它始终是 DHCP,并且不会更改为静态。因此,地址不会发生任何变化。
    • 第一个代码块中的小错字。 if (WifiConf.networkId == connectionInfo.getNetworkId()){ 应该是: if (conf.networkId == connectionInfo.getNetworkId()){ 和: WifiConf = conf;应该是:wifiConf = conf;
    • 确保你调用了 wifiManager.saveConfiguration();在 wifiManager.updateNetwork(wifiConf) 之后;正确应用设置。参考:stackoverflow.com/questions/18136000/…
    【解决方案2】:

    适用于安卓 5.1.0

          WifiConfiguration GetCurrentWifiConfiguration(WifiManager manager)
        {
        if (!manager.isWifiEnabled())
            return null;
    
        List<WifiConfiguration> configurationList = manager.getConfiguredNetworks();
        WifiConfiguration configuration = null;
        int cur = manager.getConnectionInfo().getNetworkId();
        for (int i = 0; i < configurationList.size(); ++i)
        {
            WifiConfiguration wifiConfiguration = configurationList.get(i);
            if (wifiConfiguration.networkId == cur)
                configuration = wifiConfiguration;
        }
    
        return configuration;
    }
    
    
    
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void setWifiProxySettings5()
    {
        //get the current wifi configuration
        WifiManager manager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
        WifiConfiguration config = GetCurrentWifiConfiguration(manager);
        if(null == config)
            return;
    
        try
        {
            //linkProperties is no longer in WifiConfiguration
            Class proxyInfoClass = Class.forName("android.net.ProxyInfo");
            Class[] setHttpProxyParams = new Class[1];
            setHttpProxyParams[0] = proxyInfoClass;
            Class wifiConfigClass = Class.forName("android.net.wifi.WifiConfiguration");
            Method setHttpProxy = wifiConfigClass.getDeclaredMethod("setHttpProxy", setHttpProxyParams);
            setHttpProxy.setAccessible(true);
    
            //Method 1 to get the ENUM ProxySettings in IpConfiguration
            Class ipConfigClass = Class.forName("android.net.IpConfiguration");
            Field f = ipConfigClass.getField("proxySettings");
            Class proxySettingsClass = f.getType();
    
            //Method 2 to get the ENUM ProxySettings in IpConfiguration
            //Note the $ between the class and ENUM
            //Class proxySettingsClass = Class.forName("android.net.IpConfiguration$ProxySettings");
    
            Class[] setProxySettingsParams = new Class[1];
            setProxySettingsParams[0] = proxySettingsClass;
            Method setProxySettings = wifiConfigClass.getDeclaredMethod("setProxySettings", setProxySettingsParams);
            setProxySettings.setAccessible(true);
    
    
            ProxyInfo pi = ProxyInfo.buildDirectProxy("127.0.0.1", 8118);
            //Android 5 supports a PAC file
            //ENUM value is "PAC"
            //ProxyInfo pacInfo = ProxyInfo.buildPacProxy(Uri.parse("http://localhost/pac"));
    
            //pass the new object to setHttpProxy
            Object[] params_SetHttpProxy = new Object[1];
            params_SetHttpProxy[0] = pi;
            setHttpProxy.invoke(config, params_SetHttpProxy);
    
            //pass the enum to setProxySettings
            Object[] params_setProxySettings = new Object[1];
            params_setProxySettings[0] = Enum.valueOf((Class<Enum>) proxySettingsClass, "STATIC");
            setProxySettings.invoke(config, params_setProxySettings);
    
            //save the settings
            manager.updateNetwork(config);
            manager.disconnect();
            manager.reconnect();
        }
        catch(Exception e)
        {
            Log.v("wifiProxy", e.toString());
        }
    }
    

    【讨论】:

    • 它可以在 api 23 上运行吗?我在 Android M 上尝试过,但它不工作。
    • 不知道 api 23。我在棒棒糖设备中使用此代码进行测试
    【解决方案3】:

    对于 Android 5.0+,一个 WIP 解决方案。由于某种原因,它还不能工作。欢迎评论。

    void changeWifiConfiguration(boolean dhcp, String ip, int prefix, String dns1, String gateway) {
        WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        if(!wm.isWifiEnabled()) {
            // wifi is disabled
            return;
        }
        // get the current wifi configuration
        WifiConfiguration wifiConf = null;
        WifiInfo connectionInfo = wm.getConnectionInfo();
        List<WifiConfiguration> configuredNetworks = wm.getConfiguredNetworks();   
        if(configuredNetworks != null) {
            for (WifiConfiguration conf : configuredNetworks){
                if (conf.networkId == connectionInfo.getNetworkId()){
                    wifiConf = conf;
                    break;              
                }
            }
        }
        if(wifiConf == null) {
            // wifi is not connected
            return;
        }
        try {
            Class<?> ipAssignment = wifiConf.getClass().getMethod("getIpAssignment").invoke(wifiConf).getClass();
            Object staticConf = wifiConf.getClass().getMethod("getStaticIpConfiguration").invoke(wifiConf);
            if(dhcp) {
                wifiConf.getClass().getMethod("setIpAssignment", ipAssignment).invoke(wifiConf, Enum.valueOf((Class<Enum>) ipAssignment, "DHCP"));
                if(staticConf != null) {
                    staticConf.getClass().getMethod("clear").invoke(staticConf);
                }
            } else {
                wifiConf.getClass().getMethod("setIpAssignment", ipAssignment).invoke(wifiConf, Enum.valueOf((Class<Enum>) ipAssignment, "STATIC"));
                if(staticConf == null) {
                    Class<?> staticConfigClass = Class.forName("android.net.StaticIpConfiguration");
                    staticConf = staticConfigClass.newInstance();
                }
                // STATIC IP AND MASK PREFIX
                Constructor<?> laConstructor = LinkAddress.class.getConstructor(InetAddress.class, int.class);
                LinkAddress linkAddress = (LinkAddress) laConstructor.newInstance(
                        InetAddress.getByName(ip), 
                        prefix);
                staticConf.getClass().getField("ipAddress").set(staticConf, linkAddress);
                // GATEWAY
                staticConf.getClass().getField("gateway").set(staticConf, InetAddress.getByName(gateway));
                // DNS
                List<InetAddress> dnsServers = (List<InetAddress>) staticConf.getClass().getField("dnsServers").get(staticConf);
                dnsServers.clear();
                dnsServers.add(InetAddress.getByName(dns1)); 
                dnsServers.add(InetAddress.getByName("8.8.8.8")); // Google DNS as DNS2 for safety
                // apply the new static configuration
                wifiConf.getClass().getMethod("setStaticIpConfiguration", staticConf.getClass()).invoke(wifiConf, staticConf);
            }
            // apply the configuration change
            boolean result = wm.updateNetwork(wifiConf) != -1; //apply the setting
            if(result) result = wm.saveConfiguration(); //Save it
            if(result) wm.reassociate(); // reconnect with the new static IP
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    

    【讨论】:

    • 这个拯救了我的一天
    【解决方案4】:

    @罗宾

    感谢您的解决方案在运行 Android M 6.0.1 的 My Nexus 设备上运行良好。

    我已经替换了 // apply the configuration change boolean result = wm.updateNetwork(wifiConf) != -1; //apply the setting if(result) result = wm.saveConfiguration(); //Save it if(result) wm.reassociate(); // reconnect with the new static IP

    以下

    int netId = manager.updateNetwork(wifiConf);
    boolean result =  netId!= -1; //apply the setting
    if(result){
        boolean isDisconnected =  manager.disconnect();
        boolean configSaved = manager.saveConfiguration(); //Save it
        boolean isEnabled = manager.enableNetwork(wifiConf.networkId, true);
        // reconnect with the new static IP
        boolean isReconnected = manager.reconnect();                        
    }
    

    【讨论】:

    • 谢谢。 enableNetwork 可能是缺少的位。然而,真的需要禁用所有其他网络吗?您是否也尝试过 enableNetwork/wifiConf.networkId, false)?
    【解决方案5】:

    如果您尝试在 6.x 上使用适用于 Android 5.x 的解决方案,您的应用程序将被拒绝这样做。为此,您可能需要 root 设备并将应用程序设为设备所有者。

    我对这个问题进行了一些研究,我的发现是,如果将应用程序设置为设备所有者,则过去适用于 Andrdoi 5.x 的代码可能会起作用。

    使用此处找到的示例是一个很好的示例:

    https://github.com/googlesamples/android-DeviceOwner/

    使用 adb shell 并运行命令:

    dpm set-device-owner com.example.android.deviceowner/.DeviceOwnerReceiver

    将使应用程序设备所有者并且可以设置静态IP。

    【讨论】:

      【解决方案6】:

      作为WifiConfiguration 的 kotlin 扩展,适用于 Android 5+

      fun WifiConfiguration.setHttpProxyCompat(proxyInfo: ProxyInfo) {
          if (Build.VERSION.SDK_INT >= 26) {
              httpProxy = proxyInfo
              Timber.i("Setting proxy using 26+ method")
          } else {
              val proxySettings = Class.forName("android.net.IpConfiguration\$ProxySettings")
              val valueOf = proxySettings.getMethod("valueOf", String::class.java)
              val static = valueOf.invoke(proxySettings, "STATIC")
      
              val setProxy = this::class.java.getDeclaredMethod("setProxy", proxySettings, ProxyInfo::class.java)
              setProxy.isAccessible = true
      
              setProxy.invoke(this, static, proxyInfo)
              Timber.i("Setting proxy using reflection")
          }
      }
      

      【讨论】:

        【解决方案7】:

        @Yeung,大家

        据我了解,Android 会在连接到 SSID 后立即启动 dhclient。

        所以代码建议,只有在Android已经获得IP地址(dhcp成功)之后才会应用静态配置,对吧?

        这就是我在 Pie 实验中发生的情况。我尝试通过收听 WifiManager.NETWORK_STATE_CHANGED_ACTION 来应用静态配置

        intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        ...........
        ...........
        if ( action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) )
        {
            NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            if (info.isConnectedOrConnecting())
            {
                //apply static IP to current wifi connnections as per above code
            }
        }
        

        【讨论】:

          【解决方案8】:

          @Robin 非常感谢,您的解决方案在 Android 7 上完全适用于我(必需 系统应用权限)。

          • AndroidManifest.xml
          <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:sharedUserId="android.uid.system"></manifest>
          
          • app.build.gradle
          android {
            signingConfigs {
                  system_keystore {
                      storeFile file('/Users/**/system.keystore')
                      storePassword '****'
                      keyAlias '****'
                      keyPassword '****'
                  }
              }
          
            buildTypes {
              debug {
                signingConfig signingConfigs.system_keystore
              }
              release {
                signingConfig signingConfigs.system_keystore
              }
            }
          }
          

          【讨论】:

            猜你喜欢
            • 2015-02-20
            • 2013-08-10
            • 1970-01-01
            • 1970-01-01
            • 2017-03-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多