我已查看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设置的。我可以修改一个SSID上的设置,就像在Android 2.3上一样吗?
答案 0 :(得分:59)
我意识到每个SSID的设置在3.x或4.x上没有API。因此,我查看了源代码,发现每个SSID的配置都存储在android.net.wifi.WifiConfiguration
中android.net.wifi.WifiManager
。
在下面的代码中,IpAssignment
是一个枚举,STAIC
,DHCP
或NONE
。
linkProperties
是对象存储IP地址,网关,DNS等......
linkAddress
是IP地址,其网络掩码为prefixLength(网络掩码中的位数为1)。
mRoutes
是ArrayList
的{{1}},可以指示网关。
RouteInfo
mDnses
为ArrayList
。
首先,使用InetAddress
SSID
WifiConfiguration
隐藏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;
}
}
和IpAssignment
时,可以从反射中获取对象。
以下方法可以在SSID WifiConfiguration上设置声明的IP地址设置:
linkProperties
之后,我可以为此SSID设置和更新 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));
}
。
WifiConfiguration
编辑:
很抱歉,我没有检查Android 3.x设备是否具有Android 4.x的silmilar UI。
在Android 3.x中,网关在 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();
}
mGateways
中被暂停。
linkProperties
是mGateways
类型的Arraylist
。因此,以下内容适用于Android 3.x。
InetAddress
Edit2:方法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);
}
,setIpAddress
,setGateway
应以setDNS
类型输入。
答案 1 :(得分:3)
@Robin
感谢您的解决方案在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();
}
答案 2 :(得分:2)
对于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();
}
}
答案 3 :(得分: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());
}
}
答案 4 :(得分:0)
如果您尝试在6.x上使用Android 5.x解决方案,您的应用程序将被拒绝执行此操作。要做到这一点,您可能需要根设备并使应用程序成为设备所有者。
我已经解决了一些问题,我的调查结果是,如果将应用程序设置为设备所有者,过去适用于Andrdoi 5.x的代码可能会有效。
如何完成此操作的一个很好的例子是使用此处的示例:
https://github.com/googlesamples/android-DeviceOwner/
使用adb shell并运行命令:
dpm set-device-owner com.example.android.deviceowner / .DeviceOwnerReceiver
将使应用程序设备所有者可以设置静态IP。
答案 5 :(得分:0)
作为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")
}
}
答案 6 :(得分:0)
@杨,大家
据我了解,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
}
}
答案 7 :(得分:0)
@Robin 非常感谢,您的解决方案完全适用于 Android 7(需要 系统应用权限)。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:sharedUserId="android.uid.system"></manifest>
android {
signingConfigs {
system_keystore {
storeFile file('/Users/**/system.keystore')
storePassword '****'
keyAlias '****'
keyPassword '****'
}
}
buildTypes {
debug {
signingConfig signingConfigs.system_keystore
}
release {
signingConfig signingConfigs.system_keystore
}
}
}