草庐IT

Android 12 Wifi 开发(获取列表、连接、断开连接)

LinkDamo 2023-04-15 原文

获取Wifi列表:

扫描(这个方法早在Android 9.0 就被弃用),不过如果不调用的话是没法及时获取Wifi列表的广播的。(不需要也能正常获取,没有延迟,经实验毫无区别)

public static void searchWifiList(WifiManager manager) {
        manager.startScan();
}

创建广播并接收:

/**
     * 获取附近的WiFi列表
     *
     * @param manager WifiManager
     * @param flag    是否保留重名但BSSID不同的wifi     true保留,false不保留
     * @return wifi列表
     */
    public static List<ScanResult> scanResults(WifiManager manager, boolean flag) {
        List<ScanResult> scanResults = new ArrayList<>();
        HashSet<String> hs = new HashSet<>();
        Log.d("WifiUtils", "scanResults: " + manager.getScanResults().size());
        if (flag) {
            scanResults = manager.getScanResults();
            return scanResults;
        }
        for (ScanResult scanResult : manager.getScanResults()) {
            if (hs.add(scanResult.SSID)) {
                scanResults.add(scanResult);
            }
        }
        return scanResults;
    }
private final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                Log.d("WifiFragment", "onReceive: 刷新数据");
                results = WifiUtils.scanResults(manager, true);
                mainBinding.scanResult.getAdapter().notifyDataSetChanged();
            } else if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
                switch (wifiState) {
                    case WifiManager.WIFI_STATE_DISABLED:
                        Log.d("WifiFragment", "onReceive: wifi 关闭");
                        mainBinding.switchWifi.setText("已停用");
                        results = new ArrayList<>();
                        mainBinding.scanResult.getAdapter().notifyDataSetChanged();
                        break;
                    case WifiManager.WIFI_STATE_DISABLING:
                    case WifiManager.WIFI_STATE_ENABLING:
                    case WifiManager.WIFI_STATE_UNKNOWN:
                        break;
                    case WifiManager.WIFI_STATE_ENABLED:
                        Log.d("WifiFragment", "onReceive: wifi 打开");
                        mainBinding.switchWifi.setText("已启用");
                        break;
                }
            }
        }
    };

配置并连接(无系统签名):

/**
     * 创建连接
     * @param manager WifiManager
     * @param ssid  Wifi名称
     * @param bssid 唯一标识(可以为空)
     * @param passwd    密码  (当前网络是开放网络时,可以为空)
     * @param isHidden  是否是隐藏网络
     * @param capabilities  安全协议(根据协议选择连接方式)
     */
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static void connectWifiForQ(WifiManager manager, String ssid, String bssid, String passwd, boolean isHidden, String capabilities) {
        if (capabilities.contains("WPA-PSK") || capabilities.contains("WPA2-PSK")) {
            setWPA2ForQ(manager, ssid, bssid, passwd, isHidden);
        } else {
            setESSForQ(manager, ssid, isHidden);
        }
    }

    // WPA2-PSK
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static int setWPA2ForQ(WifiManager manager, String ssid, String bssid, String passwd, boolean isHidden) {
        WifiNetworkSuggestion suggestion;
        if (bssid == null) {
            suggestion= new WifiNetworkSuggestion.Builder()
                    .setSsid(ssid)
                    .setWpa2Passphrase(passwd)
                    .setIsHiddenSsid(isHidden)
                    .build();
        } else {
            suggestion= new WifiNetworkSuggestion.Builder()
                    .setSsid(ssid)
                    .setBssid(MacAddress.fromString(bssid))
                    .setWpa2Passphrase(passwd)
                    .setIsHiddenSsid(isHidden)
                    .build();
        }
        List<WifiNetworkSuggestion> suggestions = new ArrayList<>();
        suggestions.add(suggestion);
        int status = manager.addNetworkSuggestions(suggestions);
        if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
            // 连接失败
            Log.d("WifiUtils", "setWPA2ForQ: 添加失败");
        } else {
            Log.d("WifiUtils", "setWPA2ForQ: 添加成功");
        }
        return status;
    }

    // ESS
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static int setESSForQ(WifiManager manager, String ssid, boolean isHidden) {
        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
                .setSsid(ssid)
                .setIsHiddenSsid(isHidden)
                .build();
        List<WifiNetworkSuggestion> suggestions = new ArrayList<>();
        suggestions.add(suggestion);
        int status = manager.addNetworkSuggestions(suggestions);
        if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
            // 连接失败
            Log.d("WifiUtils", "setWPA2ForQ: 添加失败");
        } else {
            Log.d("WifiUtils", "setWPA2ForQ: 添加成功");
        }
        return status;

    }

配置并连接(有系统签名):

/**
     * 连接wifi
     *
     * @param manager       WifiManager
     * @param configuration Wifi配置
     * @return 是否连接成功
     */
    public static boolean connectWifi(WifiManager manager, WifiConfiguration configuration) {
        int id = manager.addNetwork(configuration);
        WifiInfo connectionInfo = manager.getConnectionInfo();
        manager.disableNetwork(connectionInfo.getNetworkId());
        boolean b = manager.enableNetwork(id, true);
        Log.d("WifiManagerUtils", "connectWifi: 连接状态=" + b);
        if (b) {
            manager.saveConfiguration();
        } else {
            Log.d("WifiManagerUtils", configuration.toString());
        }
        return b;
    }

    /**
     * 创建Wifi配置
     *
     * @param SSID         wifi名称
     * @param password     wifi密码
     * @param hidden       网络是否隐藏(该方法与添加隐藏网络通用)
     * @param capabilities 网络安全协议
     * @return 配置好的wifi
     */
    public static WifiConfiguration createWifiInfo(String SSID, String password, boolean hidden, String capabilities) {
        WifiConfiguration configuration = new WifiConfiguration();
        configuration.SSID = "\"" + SSID + "\"";
        if (hidden) {
            configuration.hiddenSSID = true;
        }
        Log.d("WifiManagerUtils", "createWifiInfo: " + capabilities);
        if (capabilities.contains("SAE") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            setWPA3(configuration, password);
        } else if (capabilities.contains("WPA-PSK") || capabilities.contains("WPA2-PSK")) {
            setWPA(configuration, password);
        } else if (capabilities.contains("WEP")) {
            setWEP(configuration, password);
        } else {
            setESS(configuration);
        }
        return configuration;
    }

    /**
     * 设置wpa3协议
     *
     * @param configuration 配置
     * @param password      密码
     */
    public static void setWPA3(WifiConfiguration configuration, String password) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
        }
        configuration.preSharedKey = "\"" + password + "\"";
    }

    /**
     * WPA协议
     *
     * @param configuration 配置
     * @param password      密码
     */
    public static void setWPA(WifiConfiguration configuration, String password) {
        configuration.preSharedKey = "\"" + password + "\"";
        //公认的IEEE 802.11验证算法。
        configuration.allowedAuthAlgorithms.clear();
        configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
        //公认的的公共组密码。
        configuration.allowedGroupCiphers.clear();
        configuration.allowedGroupCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
        //公认的密钥管理方案。
        configuration.allowedKeyManagement.clear();
        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
        //密码为WPA。
        configuration.allowedPairwiseCiphers.clear();
        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
        //公认的安全协议。
        configuration.allowedProtocols.clear();
        configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
    }

    /**
     * WEP协议
     *
     * @param configuration 配置
     * @param password      密码
     */
    public static void setWEP(WifiConfiguration configuration, String password) {
        configuration.wepKeys[0] = "\"" + password + "\"";
        configuration.wepTxKeyIndex = 0;
        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
    }


    /**
     * 无密码
     *
     * @param configuration 配置
     */
    public static void setESS(WifiConfiguration configuration) {
        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    }

断开连接(无系统签名):

//Android11及以上可以使用,清除建议列表,可以断开当前的网络
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                List<WifiNetworkSuggestion> networkSuggestions = wifiManager.getNetworkSuggestions();
                wifiManager.removeNetworkSuggestions(networkSuggestions);
            }

监听连接状态:

 //监听网络连接状态
        connectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback(){
            @Override
            public void onAvailable(@NonNull Network network) {
                super.onAvailable(network);
                Log.d("MainActivity", "onAvailable: 网络已连接");
                Toast.makeText(MainActivity.this, "已连接网络", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onUnavailable() {
                super.onUnavailable();
                Log.d("MainActivity", "onUnavailable: 网络已断开");
                Toast.makeText(MainActivity.this, "已断开网络", Toast.LENGTH_SHORT).show();
            }
        });

注意事项:

断开当前Wifi后,再重新连接该Wifi,可能会出现无法连接的情况。

这种情况我是通过关闭Wifi后再重新打开解决的,但是对Wifi的开关控制要涉及到权限问题——

需要System权限,在Manifest中添加:

android:sharedUserId="android.uid.system"

然后需要系统签名,可在系统源码中获得。

不知道各位有没有什么好的解决方法。

项目地址:

有系统签名(推荐)——

https://github.com/Ouanu/WIFI_DEMOhttps://github.com/Ouanu/WIFI_DEMO无系统签名——

https://github.com/Ouanu/WifiDemohttps://github.com/Ouanu/WifiDemo

不需系统签名的方法(需要root):

步骤如下——

1、在/data目录下创建一个文本,用来标识Wifi是打开或者关闭状态,比如:

文本文件名字:node

内容:0                // (0是关,1是开)

2、chmod 666 /data/node (赋予该文件的读写权限)

3、创建循环检测该文件内容的脚本,并针对内容执行adb命令

adb shell svc wifi enable    // 开启WIFI
adb shell svc wifi disable    // 关闭WIFI

4、应用端(App)对该文本进行读写

即可实现不需系统签名也可控制WIFI模块的开关

有关Android 12 Wifi 开发(获取列表、连接、断开连接)的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  3. ruby - RVM 使用列表[0] - 2

    是否有类似“RVMuse1”或“RVMuselist[0]”之类的内容而不是键入整个版本号。在任何时候,我们都会看到一个可能包含5个或更多ruby的列表,我们可以轻松地键入一个数字而不是X.X.X。这也有助于rvmgemset。 最佳答案 这在RVM2.0中是可能的=>https://docs.google.com/document/d/1xW9GeEpLOWPcddDg_hOPvK4oeLxJmU3Q5FiCNT7nTAc/edit?usp=sharing-知道链接的任何人都可以发表评论

  4. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  5. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  6. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  7. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  8. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  9. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  10. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

随机推荐