web-dev-qa-db-fra.com

Comment configurer une adresse IP statique, un masque de réseau, une passerelle par programme sur Android 3.x ou 4.x

J'ai vérifié la question de débordement de pile API permettant de configurer des adresses IP statiques dans une application Android.

Cela fonctionne jusqu'à Android 2.3. Cependant, il n'y a pas de chance sur un niveau supérieur d'API. Par exemple, .__ je mets le paramètre

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");

Mais je reviens pour vérifier par:

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

Le champ IP Settings est toujours indiqué DHCP mais pas Static

Il est vrai que je peux utiliser Android.provider.Settings.System.getString() pour récupérer ce que j'ai défini. Cela prouve que le paramètre est enregistré quelque part mais que le système l'ignore simplement. 

Le système utilise un paramètre autre que Android.provider.Settings.System sous Android 3.x et 4.x, car il est défini par SSID du point d'accès. Puis-je modifier les paramètres sur un SSID exactement comme cela fonctionne sur Android 2.3?

30
Yeung

Je me rends compte qu'il n'y a pas d'API sur 3.x ou 4.x pour ceux configurés par SSID. Par conséquent, j'ai vérifié le code source et découvert que la configuration de chaque SSID est stockée dans Android.net.wifi.WifiConfiguration qui provient de Android.net.wifi.WifiManager.

Dans le code ci-dessous, IpAssignment est une énumération, soit STAIC, DHCP ou NONE. Et linkProperties correspond à l'adresse IP, à la passerelle, au DNS, etc. du magasin d'objets.

linkAddress est l'adresse IP et son masque de réseau sous la forme prefixLength (combien de bits 1 dans le masque de réseau).

mRoutes est ArrayList of RouteInfo qui peut indiquer une passerelle.

mDnses est ArrayList of InetAddress pour DNS.

Tout d'abord, obtenez la configuration actuelle en utilisant 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;              
    }
}

Comme les variables IpAssignment et linkProperties sont masquées, l'objet peut être obtenu par réflexion.

La méthode suivante peut définir le paramètre d'adresse IP déclaré sur la configuration de SSID WifiConfiguration:

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

Après cela, je peux définir et mettre à jour WifiConfiguration pour ce SSID. 

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

Edit: Désolé, je ne vérifie pas les appareils Android 3.x dotés d'une interface utilisateur silmilar avec Android 4.x . Sous Android 3.x, la passerelle est interrompue dans mGateways de linkProperties.mGateways est Arraylist de type InetAddress. Par conséquent, la suite devrait fonctionner dans 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: Les méthodes setIpAddress, setGateway, setDNS doivent être entrées en tant que type InetAddress.

58
Yeung

@Robin 

Merci, votre solution fonctionne bien pour moi sur mon appareil Nexus fonctionnant sous Android M 6.0.1.

J'ai remplacé le // 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

avec ce qui suit 

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

Pour Android 5.0+, une solution WIP. Cela ne fonctionne pas encore pour une raison quelconque. Commentaires bienvenus.

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

Pour 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());
    }
}
1
Jackson Chengalai

En tant qu'extension kotlin de WifiConfiguration, travaillant sous 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")
    }
}
0
crgarridos

Si vous essayez d'utiliser la solution pour Android 5.x sur 6.x, votre application sera refusée. Pour ce faire, vous devez probablement rooter votre appareil et faire de l'application le propriétaire de l'appareil.

Je me suis plongé dans le problème et mes conclusions sont que le code qui fonctionnait auparavant pour Andrdoi 5.x pourrait fonctionner si l'application est configurée pour être le propriétaire du périphérique.

Un bon exemple de la façon dont cela est fait est d'utiliser l'exemple trouvé ici:

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

En utilisant adb Shell et en exécutant la commande:

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

rendra le propriétaire du périphérique d'application et il est possible de définir une adresse IP statique.

0
Knubo