Android Wi-Fi网络连接API

一、背景
从Android Q开始,原WiFi操作API部分被废弃,eg: enableNetwork、getConfiguredNetworks等。取而代之的是WifiNetworkSpecifier,WifiNetworkSuggestion等新API(主要从安全和用户体验的角度出发,废弃了之前太过底层的API)。
二、新版API简介
Android Q增加了对点对等连接的支持。此功能使您的应用可以通过使用WifiNetworkSpecifier 描述所请求网络的属性来提示用户更改设备所连接的接入点。对等连接用于非网络提供目的,例如Chromecast和Google Home硬件等辅助设备的引导配置。

使用此API时,您将使用以下流程:

  1. 使用创建Wi-Fi网络说明符 WifiNetworkSpecifier.Builder

  2. 设置网络过滤器以匹配要连接的网络以及所需的凭据。

  3. 决定的组合SSIDSSID patternBSSID,和BSSID pattern 设置在每个请求之后,网络过滤器,必须符合以下要求:

    • 每个请求应当提供的至少一个SSIDSSID patternBSSID,或BSSID pattern
    • 每个请求只能设置一个SSIDSSID pattern
    • 每个请求只能设置一个BSSIDBSSID pattern
  4. 将说明符与NetworkCallback 实例一起添加到网络请求中 以跟踪请求的状态。

    如果用户接受请求并且与网络的连接成功, NetworkCallback.onAvailable() 则在回调对象上调用。如果用户拒绝请求或者与网络的连接不成功, NetworkCallback.onUnavailable() 则在回调对象上调用。

点对点连接不需要位置或Wi-Fi权限。发起连接到对等设备的请求会在同一设备上启动一个对话框,该设备的用户可以从该对话框接受连接请求。

绕过用户批准

一旦用户批准网络连接以响应来自特定应用的请求,该设备就存储对特定接入点的批准。如果应用程序再次发出连接到该访问点的特定请求,则设备将跳过用户批准阶段并自动连接到网络。如果用户选择在连接到API请求的网络时忘记网络,则会删除此应用程序和网络组合的存储批准,并且应用程序的任何将来请求都需要再次由用户批准。如果应用程序发出非特定(例如使用SSID或BSSID模式)请求,则用户将需要批准该请求。
三、实现方式
官方适用于对等连接的 WLAN 网络请求 API | Android Developers (google.cn)
但试过没有成功,其他较多的实现方式主要有两种:
一、手机WIFI设置界面

          startActivity(new Intent( android.provider.Settings.ACTION_WIFI_SETTINGS));
         //或者
          startActivity(new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY));

第二种方式就是降低API为28

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.zyd.androidwifi"
        minSdk 26
        targetSdk 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

java实现示例:

public void wifiBeforeQ(String wifiName, String wifiPwd) {
    if (mWifiManager == null) {
        Log.i(TAG, " ***** init first ***** ");
        return;
    }

    String mWifiName = "\"" + wifiName + "\"";

    /**
     * 判断定位权限
     */
    if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

        return;
    }
    //获取wifi列表
    List wifiList = mWifiManager.getConfiguredNetworks();
    boolean bFindInList = false;
    for (int i = 0; i < wifiList.size(); ++i) {
        WifiConfiguration wifiInfo0 = (WifiConfiguration) wifiList.get(i);

        // 先找到对应的wifi
        if (mWifiName.equals(wifiInfo0.SSID) || wifiName.equals(wifiInfo0.SSID)) {
            // 1、 先启动,可能已经输入过密码,可以直接启动
            Log.i(TAG, " set wifi 1 = " + wifiInfo0.SSID);
            doChange2Wifi(wifiInfo0.networkId);
            return;
        }
    }

    // 2、如果wifi还没有输入过密码,尝试输入密码,启动wifi
    if (!bFindInList) {
        WifiConfiguration wifiNewConfiguration = createWifiInfo(wifiName, wifiPwd);//使用wpa2的wifi加密方式
        int newNetworkId = mWifiManager.addNetwork(wifiNewConfiguration);
        if (newNetworkId == -1) {
            Log.e(TAG, "操作失败,需要您到手机wifi列表中取消对设备连接的保存");
        } else {
            doChange2Wifi(newNetworkId);
        }
    }
}

第三种不降API实现方式(不过没有成功)

@RequiresApi(api = Build.VERSION_CODES.Q)
public void wifiAfterQ(String wifiName, String wifiPwd) {
    Log.i(TAG, " ***** changeToWifiAfterQ first ***** ");
    if (mWifiManager == null || connectivityManager == null) {
        Log.i(TAG, " ***** init first ***** ");
        return;
    }

    /**
     * 判断定位权限
     */
    if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

        return;
    }
    //获取wifi扫描列表
//        List<ScanResult> wifiList = mWifiManager.getScanResults();
//        ScanResult scan = null;
//        for (ScanResult scanResult : wifiList) {
//            if (wifiName.equals(scanResult.SSID)) {
//                scan = scanResult;
//                break;
//            }
//        }
    //扫描到了Wi-Fi
//        if (null != scan) {
    //setSsidPattern/setSsid/setBssidPattern/setBssid should be invoked for specifier
    NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
//                        .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX))
            .setSsid(wifiName)
            .setWpa2Passphrase(wifiPwd)
//                .setBssid(MacAddress.fromString(scan.BSSID))
            .build();

    NetworkRequest request =
            new NetworkRequest.Builder()
                    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    .setNetworkSpecifier(specifier)
                    .build();

    networkCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(@NonNull Network network) {
            super.onAvailable(network);
            Log.i(TAG, "onAvailable 切换到指定wifi成功"+network);
            connectivityManager.bindProcessToNetwork(network);
            connectivityManager.getNetworkInfo(network);
            NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
            boolean connected = networkInfo.isConnected();

            boolean available = networkInfo.isAvailable();
            NetworkInfo.DetailedState detailedState = networkInfo.getDetailedState();
            Log.i(TAG, "onAvailable connected= "+connected);
            Log.i(TAG, "onAvailable available= "+available);
            Log.i(TAG, "onAvailable detailedState= "+detailedState);

        }

        @Override
        public void onUnavailable() {
            super.onUnavailable();
            Log.i(TAG, "onUnavailable 切换到指定wifi失败");
            connectivityManager.unregisterNetworkCallback(networkCallback);
        }
    };
 // connectivityManager.registerNetworkCallback(request, networkCallback);
    connectivityManager.requestNetwork(request, networkCallback);

//        }else{
//            Log.e(TAG, "未找到目标Wi-Fi");
//        }

}

四、Wi-Fi Easy连接
通过Android Q,您可以使用Easy Connect为对等设备配置Wi-Fi凭据,以替代已弃用的WPS。应用可以使用ACTION_PROCESS_WIFI_EASY_CONNECT_URIintent 将Easy Connect集成到其设置和配置流程中 。此意图需要URI。调用应用程序可以通过各种方法检索URI,包括从贴纸或显示器扫描QR码,或通过扫描蓝牙LE或NFC广告。

URI可用后,您可以使用ACTION_PROCESS_WIFI_EASY_CONNECT_URIintent 配置对等设备的Wi-Fi凭据。这允许用户选择要共享的Wi-Fi网络并安全地传输凭证。

Easy Connect不需要位置或Wi-Fi权限。

注意:在使用此意图之前,应用程序必须通过调用验证设备是否支持Easy ConnectWifiManager.isEasyConnectSupported()

五、Wi-Fi Direct连接API
在WifiP2pConfig和WifiP2pManagerAPI类在Android Q的更新,以支持使用预定信息的快速连接建立功能的Wi-Fi直连。该信息通过侧通道共享,例如蓝牙或NFC。

以下代码示例显示如何使用预定信息创建组:
Kotlin语言写法

val manager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
val channel = manager.initialize(this, mainLooper, null)

// prefer 5G band for this group
val config = WifiP2pConfig.Builder()
.setNetworkName("networkName")
.setPassphrase("passphrase")
.enablePersistentMode(false)
.setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_5GHZ)
.build()

// create a non-persistent group on 5GHz
manager.createGroup(channel, config, null)

Java语言写法

WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(this, getMainLooper(), null);

// prefer 5G band for this group
WifiP2pConfig config = new WifiP2pConfig.Builder()
.setNetworkName("networkName")
.setPassphrase("passphrase")
.enablePersistentMode(false)
.setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_5GHZ)
.build();

// create a non-persistent group on 5GHz
manager.createGroup(channel, config, null);

要使用凭据加入组,请使用manager.createGroup()以下内容替换:

Kotlin语言写法

manager.connect(channel, config, null)

Java语言写法

manager.connect(channel, config, null);

参考Android Q(10.0)的新功能和API

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容