如何在Android 3.x或4.x上通过编程配置静态IP地址、子网掩码和网关。

31

我查看了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设置”字段仍显示为“DHCP”,而不是“静态”。

确实,我可以使用android.provider.Settings.System.getString()获取我设置的内容。这证明该设置已保存在某个地方,但系统只是忽略了它。

在Android 3.x和4.x上,系统使用除 android.provider.Settings.System 之外的设置作为每个接入点SSID的设置。我能否像Android 2.3中那样在一个SSID上修改设置?

8个回答

61
我知道在3.x或4.x上没有为每个SSID设置API。因此,我查看了源代码,并发现每个SSID的配置存储在android.net.wifi.WifiConfiguration中,该对象可以通过android.net.wifi.WifiManager获取。
在下面的代码中,IpAssignment是一个枚举,可以是STAICDHCPNONE。而linkProperties是存储IP地址、网关、DNS等信息的对象。 linkAddress是IP地址及其前缀长度(即子网掩码中有多少个1)。 mRoutes是可指示网关的RouteInfoArrayListmDnses是DNS的InetAddressArrayList
首先,使用WifiConfiguration SSID获取当前配置。
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 3.x 设备,它们与 Android 4.x 拥有类似的用户界面。 在 Android 3.x 中,网关存储在 linkPropertiesmGateways 中。 mGateways 是类型为 InetAddressArraylist。因此,在 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);
        }

编辑2: 方法setIpAddress, setGateway, setDNS 应该以InetAddress类型作为输入。


2
它真的能工作吗?我已经尝试过3.x甚至4.x版本,但没有效果。它总是DHCP,而不会改变为静态。因此,地址没有发生变化。 - Harpreet
当调用wifiManager.updateNetowrk(wifiConf)时,我遇到了一个空指针异常。你有什么想法吗? - Mr.Noob
5
第一个代码块中有一个小错误。原文:if (WifiConf.networkId == connectionInfo.getNetworkId()){应该改为:if (conf.networkId == connectionInfo.getNetworkId()){还有:WifiConf = conf;应该改为:wifiConf = conf; - dubmojo
2
请确保在 wifiManager.updateNetwork(wifiConf) 后调用 wifiManager.saveConfiguration(); 以正确应用设置。 参考:https://dev59.com/yHXYa4cB1Zd3GeqP6ndH - M. Usman Khan
当手机使用3G连接时,我该如何更改DNS? - Emre Koç
显示剩余21条评论

4

@Robin

谢谢你的解决方案,它对我在运行Android M 6.0.1的Nexus设备上的情况非常有效。

我已经将以下内容替换为:

//应用配置更改 boolean result = wm.updateNetwork(wifiConf) != -1; //应用设置 if(result) result = wm.saveConfiguration(); //保存设置 if(result) wm.reassociate(); //使用新的静态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();                        
}

1
感谢您。enableNetwork可能是缺失的部分。但是真的需要禁用所有其他网络吗?您是否也尝试过使用enableNetwork / wifiConf.networkId,false)? - Robin Gawenda

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();
    }
}

1
对于Android 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上尝试了一下,但没有运行。 - shinilms
不了解API 23。我在Lollypop设备上使用此代码进行测试。 - Jackson Chengalai

0

如果您尝试在Android 6.x上使用适用于5.x的解决方案,则可能会被拒绝。要做到这一点,您可能需要对设备进行root,并使应用程序成为设备所有者。

我已经深入研究了这个问题,我的发现是,对于以前适用于Andrdoi 5.x的代码,如果将应用程序设置为设备所有者,则可能会起作用。

如何完成此操作的一个很好的例子是使用此处找到的示例:

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

使用adb shell并运行以下命令:
dpm set-device-owner com.example.android.deviceowner/.DeviceOwnerReceiver 将使应用程序成为设备所有者,并且可以设置静态IP。

0

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

0
作为 Android 5+ 的 Kotlin 扩展,它是 WifiConfiguration 的一部分。
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")
    }
}

0

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接