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?
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
.
@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();
}
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();
}
}
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());
}
}
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")
}
}
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.