26、权限与WiFi:ACCESS_WIFI_STATE、CHANGE_WIFI_STATE、附近设备权限

WiFi相关的权限,说实话,是Android权限体系里一个很有意思的缩影。你想想看,从最早的几个简单权限,到后来为了隐私保护不断拆分、加限制,这背后其实是整个行业对用户数据认知的升级。我个人习惯把WiFi权限分成两类来看:一类是“看看就行”的查询权限,另一类是“动手操作”的控制权限。嗯,咱们今天就把它彻底聊透。

一、ACCESS_WIFI_STATE:只读,但信息量不小

这个权限,说白了就是让你的App能“看一眼”当前WiFi的状态。它能拿到什么?

  • WiFi是否开启
  • 当前连接的SSID(也就是WiFi名字)
  • BSSID(路由器的MAC地址)
  • 信号强度(RSSI)
  • IP地址、网关、DNS等信息

我在项目中遇到过一个问题:有个做室内定位的App,需要根据WiFi信号强度来推算用户位置。当时我们只申请了ACCESS_WIFI_STATE,结果发现Android 10之后,拿到的SSID全是null。为什么?因为系统要求必须同时拥有ACCESS_FINE_LOCATION权限才能获取真实的WiFi列表和SSID。这个坑,我印象特别深。

重要提醒:从Android 9(API 28)开始,ACCESS_WIFI_STATE本身不再能获取到SSID/BSSID。你需要位置权限(ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION)才能拿到这些信息。

代码示例很简单,但要注意运行时检查:

// 检查WiFi状态
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (checkSelfPermission(Manifest.permission.ACCESS_WIFI_STATE) 
        == PackageManager.PERMISSION_GRANTED) {
    int rssi = wifiManager.getConnectionInfo().getRssi();
    // 信号强度,单位dBm
    Log.d("WiFiDemo", "当前信号强度: " + rssi);
} else {
    // 申请权限
    requestPermissions(new String[]{Manifest.permission.ACCESS_WIFI_STATE}, REQUEST_CODE);
}

二、CHANGE_WIFI_STATE:动手开关WiFi

这个权限比上一个“重”一些。它允许你的App去改变WiFi的状态——比如打开或关闭WiFi、连接某个热点、忘记某个网络。听起来挺强大,对吧?但实际使用中,限制也不少。

我曾经做过一个自动化工具App,功能是“到家自动连WiFi,出门自动关WiFi”。当时我天真地以为,有了CHANGE_WIFI_STATE就能为所欲为。结果发现,从Android 10开始,系统对WiFi的开关操作做了严格限制——你的App必须在前台,并且用户必须明确授权,才能通过WifiManager.setWifiEnabled()来开关WiFi。

注意:在Android 10+上,setWifiEnabled()方法已经被标记为废弃。系统推荐使用Settings.Panel.ACTION_WIFI来引导用户手动操作,或者使用WifiNetworkSpecifier来连接特定网络。

来看看怎么连接一个指定的WiFi网络(Android 10+推荐方式):

// 使用WifiNetworkSpecifier连接特定WiFi
WifiNetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
        .setSsid("MyHomeWiFi")
        .setWpa2Passphrase("password123")
        .build();

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

ConnectivityManager connectivityManager = 
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(@NonNull Network network) {
        // 连接成功
        Log.d("WiFiDemo", "已连接到指定WiFi");
    }

    @Override
    public void onUnavailable() {
        // 连接失败
        Log.d("WiFiDemo", "无法连接到指定WiFi");
    }
};

connectivityManager.requestNetwork(request, callback);

嗯,这里要注意:这个方式会弹出一个系统对话框,让用户确认是否要连接这个网络。你不能在后台静默连接,这是为了保护用户隐私和安全。

三、附近设备权限(NEARBY_DEVICES):Android 12的新成员

Android 12引入了一个新权限——NEARBY_DEVICES(属于NEARBY_WIFI_DEVICES权限组)。这个权限的出现,说实话,解决了一个长期存在的尴尬问题。

你想想看,以前App想扫描附近的WiFi设备(比如做WiFi直连、热点发现),必须申请位置权限。但App其实并不需要知道用户的具体位置,它只是想找找周围有没有可用的设备。这就导致了一个矛盾:为了一个简单的设备发现功能,用户必须把位置信息交出去。很多用户不理解,觉得“你一个WiFi工具为什么要我的位置?”

所以Google在Android 12里做了拆分:如果你只是想做WiFi设备发现、WiFi直连、或者热点扫描,不需要位置信息,只需要申请NEARBY_DEVICES权限就够了。

场景 Android 12之前 Android 12+
扫描WiFi热点列表 需要位置权限 需要NEARBY_DEVICES权限
获取当前WiFi的SSID 需要位置权限 需要位置权限(不变)
WiFi直连(P2P) 需要位置权限 需要NEARBY_DEVICES权限
开关WiFi 需要CHANGE_WIFI_STATE 需要CHANGE_WIFI_STATE(不变)

代码中声明这个权限的方式:

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />

<!-- 如果App的目标API < 33,需要添加以下声明 -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
    android:maxSdkVersion="32" />

运行时申请:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (checkSelfPermission(Manifest.permission.NEARBY_WIFI_DEVICES)
            != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(
            new String[]{Manifest.permission.NEARBY_WIFI_DEVICES},
            REQUEST_CODE_NEARBY
        );
    }
}

四、知识体系梳理

我把这三个权限的关系画了一张图,方便你理解它们各自负责的领域:

WiFi权限体系结构图 ACCESS_WIFI_STATE 查询WiFi状态 · 获取WiFi开关状态 · 获取IP/网关信息 · 获取信号强度(RSSI) CHANGE_WIFI_STATE 控制WiFi状态 · 打开/关闭WiFi · 连接指定网络 · 忘记已保存网络 NEARBY_DEVICES 发现附近设备 · 扫描WiFi热点列表 · WiFi直连(P2P) · 热点发现 权限使用建议 ✅ 只查询不修改 → 仅申请 ACCESS_WIFI_STATE ✅ 需要开关/连接WiFi → 申请 CHANGE_WIFI_STATE ✅ 需要扫描附近设备(Android 12+) → 申请 NEARBY_DEVICES ⚠ 获取SSID/BSSID → 仍需位置权限(ACCESS_FINE_LOCATION)

五、避坑指南与最佳实践

做了这么多年Android安全,我总结了几条关于WiFi权限的“血泪教训”:

  • 不要过度申请权限。 如果你只需要知道WiFi是否开启,就别申请CHANGE_WIFI_STATE。用户现在对权限很敏感,多一个权限就多一分被卸载的风险。
  • 注意Android版本差异。 同一个功能,在不同Android版本上需要的权限可能完全不同。我建议你在代码里做版本判断,而不是一刀切。
  • 权限申请要讲清楚理由。 用户拒绝权限时,不要反复弹窗。用shouldShowRequestPermissionRationale()来判断是否需要解释。
我的小技巧:在申请NEARBY_DEVICES权限时,可以这样向用户解释:“我们需要发现附近的WiFi设备,以便帮您快速连接。这个权限不会获取您的位置信息。”——用户听到“不需要位置”,接受度会高很多。

我曾经遇到一个案例:某个App在Android 12上崩溃了,原因是它还在用旧的方式扫描WiFi列表,没有适配新的NEARBY_DEVICES权限。用户反馈说“一打开就闪退”,排查了半天才发现是权限问题。从那以后,我每次适配新系统版本,第一件事就是检查权限变化。

嗯,WiFi权限这块其实不难,但细节很多。你只要记住一个原则:按需申请,最小化权限,适配版本差异。做到这三点,基本不会出大问题。


公众号:蓝海资料掘金营,微信deep3321