在Android 5 (L)上以编程方式更改WiFi配置

6

在Android 4.0.2..4.4.4上工作的代码存在一些问题,但在Android 5上并没有真正起作用,我不知道为什么。基本上,以下代码允许设置新的WiFi IP分配类型:STATIC或DHCP。我使用的代码完全涵盖在这个答案中:https://dev59.com/mGkv5IYBdhLWcg3w40wY#10309323

我将尝试在此处放置代码的最重要部分和输出信息。

...
WifiConfigurator.setIpAssignment("STATIC", wifiConf);
...

其中wifiConf是

public static WifiConfiguration getCurrentWiFiConfiguration(Context context) {
    WifiConfiguration wifiConf = null;
    ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
    if (networkInfo.isConnected()) {
        final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        final WifiInfo connectionInfo = wifiManager.getConnectionInfo();
        if (connectionInfo != null && !TextUtils.isEmpty(connectionInfo.getSSID())) {
            List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();
            if(configuredNetworks != null){
                for (WifiConfiguration conf : configuredNetworks) {
                    if (conf.networkId == connectionInfo.getNetworkId()) {
                        wifiConf = conf;
                        break;
                    }
                }
            }
        }
    }
    return wifiConf;
}

所以WifiConfigurator.setIpAssigment()调用了以下代码:
public static void setIpAssignment(String assign, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {

    setEnumField(wifiConf, assign, "ipAssignment");
}

public static void setEnumField(Object obj, String value, String name)
        throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    Log.d("myApp", obj.getClass().toString());
    Field f = obj.getClass().getField(name);
    f.set(obj, Enum.valueOf((Class<Enum>) f.getType(), value));
}

但是由于某些原因,无法找到“ipAssignment”字段:

11-30 12:40:54.343    5941-5941/com.myApp D/myApp﹕ class android.net.wifi.WifiConfiguration
11-30 12:40:54.344    5941-5941/com.myApp D/myApp﹕ Can't update network configuration. java.lang.NoSuchFieldException: ipAssignment
            at java.lang.Class.getField(Class.java:1048)
            at com.myApp.WifiConfigurator.setEnumField(WifiConfigurator.java:141)
            at com.myApp.WifiConfigurator.setIpAssignment(WifiConfigurator.java:25)
            at com.myApp.WifiConfigurator.updateWifiNetwork(WifiConfigurator.java:220)
            at com.myApp.ui.MainScreen.onAsyncTaskCompleted(MainScreen.java:251)
            at com.myApp.myAPI$UpdateIPTask.onPostExecute(myAPI.java:257)
            at com.myApp.myAPI$UpdateIPTask.onPostExecute(myAPI.java:194)
            at android.os.AsyncTask.finish(AsyncTask.java:632)
            at android.os.AsyncTask.access$600(AsyncTask.java:177)
            at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

起初,我认为谷歌改变了该字段的名称。然后我检查了Android L预览源代码,发现该字段确实存在。我不明白为什么会出现这种情况。
更新:
感谢Matiash提供的一些信息,我能够更新ipAssignment类型,但我无法更新DNS。要更新DNS,ipAssigment应该是静态的。根据这个,谷歌停止使用LinkProperties进行静态配置,现在使用StaticIpConfiguration类。
所以我该怎么做:
public static void setDNS(InetAddress dns1, InetAddress dns2, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException,
        NoSuchFieldException, IllegalAccessException {

    Object linkProperties = null;
    ArrayList<InetAddress> mDnses;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        staticIpConf = wifiConf.getClass().getMethod("getStaticIpConfiguration").invoke(wifiConf);
        mDnses = (ArrayList<InetAddress>) getDeclaredField(staticIpConf, "dnsServers");
    }
    else{
        linkProperties = getField(wifiConf, "linkProperties");
        mDnses = (ArrayList<InetAddress>) getDeclaredField(linkProperties, "mDnses");
    }

    mDnses.clear();
    mDnses.add(dns1);
    mDnses.add(dns2);
}
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;
}

下一步我在错误日志中看到的是:
12-22 09:00:49.854  25815-25815/com.myapp D/myapp﹕ Can't update network configuration. java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
            at com.myapp.WifiConfigurator.getDeclaredField(WifiConfigurator.java:245)
            at com.myapp.WifiConfigurator.setDNS(WifiConfigurator.java:78)
            at com.myapp.WifiConfigurator.updateWifiNetwork(WifiConfigurator.java:356)

我的猜测是在调用staticIpConfiguration时它不存在。我必须以某种方式初始化它。有什么想法如何更新DNS?


为了确保你正在查看正确的源代码,也许你可以尝试在一个快速测试中读取该类中的所有字段,并查看它具有哪些字段。 - Ingo Bürk
1个回答

7
尽管在L版本(至少在grepcode中的版本)中仍然存在 ipAssignment 字段,但在发布版本中已经不存在,您可以在源代码的“master”分支Github镜像中看到。

枚举定义和字段现在都在一个内部对象 IpConfiguration (也是新的)中。这就是使用反射访问未记录水域的危险所在... :)

但是,将代码进行微调以通过反射访问并在那里设置它非常简单:

public static void setIpAssignment(String assign, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Object ipConfiguration = wifiConf.getClass().getMethod("getIpConfiguration").invoke(wifiConf);
        setEnumField(ipConfiguration, assign, "ipAssignment");
    } else {
        setEnumField(wifiConf, assign, "ipAssignment");
    }
}

这段代码“有效”(在不抛出异常的情况下),但我没有进一步测试它。
< p >< em >请参阅如何为Android 5.x(Lollipop)为WiFi连接以编程方式配置静态IP地址、子网掩码、网关和DNS,以获取更详细的解决方案。


它有效了,感谢您的帮助。你会得到你的赏金 :)。但是我在代码方面还有更多问题。这段代码用于更改Wi-Fi设置(DNS IP是我最关心的设置)。如果您有任何想法如何在Android L上更改DNS IP,请告诉我 :)。 - KennyPowers
现在我得到了 java.lang.NoSuchFieldException: linkProperties,我敢打赌我会得到更多的错误。我在Android L代码中找不到正确的替代方法。 - KennyPowers
抱歉,我还不理解。缺乏经验:(。你能给我一个例子吗? - KennyPowers
@matiash 有人能让它与StaticIpConfiguration一起工作吗?我在Android 5.0上遇到了同样的问题。感谢任何帮助。你能分享另一个问题的链接,答案已经发布了吗? - Sushil
@matiash,谢谢您的回复。那真的很有帮助。 - Sushil
显示剩余4条评论

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