任何具有 INTERNET 权限的应用都可以访问局域网 (LAN) 中的设备。这使得应用可以轻松连接到本地设备,但也带来了隐私方面的影响,例如形成用户指纹和成为位置信息代理。
本地网络保护项目旨在通过在新的运行时权限后限制对本地网络的访问,来保护用户的隐私。
影响
在 Android 16 期间,此权限是一项选择启用功能,这意味着只有选择启用的应用会受到影响。选择启用该功能的目标是让应用开发者了解应用的哪些部分依赖于隐式本地网络访问权限,以便他们为在未来的 Android 版本中对这些部分进行权限保护做好准备。
如果应用使用以下方式访问用户的本地网络,则会受到影响:
- 直接或通过库在本地网络地址(例如
Multicast DNS (mDNS)或Simple Service Discovery Protocol (SSDP))上使用原始套接字。 - 使用可访问本地网络的框架级类,例如
NsdManager。
影响详情
与本地网络地址之间的流量需要本地网络访问权限。下表列出了一些常见情况:
| 应用低级层网络操作 | 需要本地网络权限 |
|---|---|
| 建立出站 TCP 连接 | 是 |
| 接受传入的 TCP 连接 | 是 |
| 发送 UDP 单播、多播、广播 | 是 |
| 接收传入的 UDP 单播、多播、广播 | 是 |
这些限制是在网络堆栈深处实现的,因此适用于所有网络 API。这包括在平台或受管理的代码中创建的套接字、Cronet 和 OkHttp 等网络库,以及基于这些库实现的任何 API。尝试解析具有 .local 后缀的本地网络上的服务需要本地网络权限。
上述规则的例外情况:
- 如果设备的 DNS 服务器位于本地网络上,则进出该服务器(在端口 53 上)的流量不需要本地网络访问权限。
- 使用输出源切换器作为其应用内选择器的应用将不需要本地网络权限(更多指南将在后续版本中发布)。
Android 17 强制执行
从 Android 17 开始,必须为以 Android 17 或更高版本为目标平台的应用强制执行本地网络保护。
| 方面 | Android 16 | Android 17 |
|---|---|---|
| 目标 SDK 版本 | 36 | 37 或更高版本 |
| 权限 | 暂时使用 NEARBY_WIFI_DEVICES | ACCESS_LOCAL_NETWORK |
| 默认访问权限 | 本地网络访问权限已开启 | 默认情况下,所有更新了目标 SDK 的应用都会被屏蔽本地网络 |
| 权限组 | 属于现有的 NEARBY_DEVICES 权限组 |
为了验证在强制执行后应用功能是否未中断,以 SDK 37 或更高版本为目标平台的应用必须采用以下任一方式来管理本地网络访问权限:
路径 A:使用可保护隐私的选择器
对于系统介导的发现和连接任务,请使用选择器,以避免完全请求广泛的运行时权限。 根据您的使用情形,使用以下选择器:
- 媒体内容流式传输:对于支持 Google Cast 的应用,它们可以使用输出切换器功能。这样,开发者便可允许用户选择特定的流式传输设备,而无需应用请求广泛的
ACCESS_LOCAL_NETWORK权限。 - 常规连接:
NsdManager包含一个用于 mDNS 发现的系统运行服务选择器。系统不会让应用扫描整个网络,而是会显示一个对话框,让用户选择单个设备供应用访问。
val discoveryRequest = DiscoveryRequest.Builder("_http._tcp")
.setFlags(DiscoveryRequest.FLAG_SHOW_PICKER)
.build()
nsdManager.registerServiceInfoCallback(discoveryRequest, executor, object : NsdManager.ServiceInfoCallback {
override fun onServiceUpdated(serviceInfo: NsdServiceInfo) {
// Handle the user-selected and discovered service
// NsdServiceInfo.getHostAddresses() can now be connected to
// without ACCESS_LOCAL_NETWORK permission
}
})
路径 B:请求运行时权限(广泛访问权限)
对于需要广泛、持续访问本地网络的复杂使用情形(例如家居自动化或物联网设备管理),此路径是必需的。
在清单中声明权限:开发者必须在
AndroidManifest.xml中明确声明ACCESS_LOCAL_NETWORK。在运行时请求权限:在尝试任何本地网络访问之前,应用必须检查是否已授予权限。否则,必须调用
Activity.requestPermission()来触发标准系统提示。预授予场景:
ACCESS_LOCAL_NETWORK权限是NEARBY_DEVICES权限组的一部分。如果用户已授予此组中的另一权限(例如蓝牙权限),系统不会再次提示用户授予本地网络访问权限。处理拒绝和撤消情况:应用必须以适当方式处理用户拒绝请求或日后在系统设置中撤消权限的情况。在这种情况下,本地网络流量将被屏蔽。
权限请求重置计数器策略
平台实现了一种计数器重置策略,用于解决以下情形:应用之前拒绝了 NEARBY_DEVICES 权限组(现在包括 ACCESS_LOCAL_NETWORK),导致应用在充分说明理由后无法请求该权限。此机制为应用调用 requestPermission() API 提供了更多机会,从而有效地重置了 ACCESS_LOCAL_NETWORK 权限的拒绝次数。这样可以更细致地重新吸引用户,尤其是在应用能够传达其核心功能需要本地网络访问权限之前,用户就拒绝了该权限的情况下。
拆分权限模型
本地网络权限采用拆分权限迁移策略,根据新应用和旧应用的目标 SDK 版本以不同的方式处理它们。
| 类别 | 目标 SDK 级别 | 本地网络访问权限行为 | 需要开发者采取行动 |
|---|---|---|---|
| 新应用 / 更新后的应用 | >= 37(Android 17) | 默认情况下已屏蔽 | 声明并请求 ACCESS_LOCAL_NETWORK 运行时权限 |
| 旧版应用 | < 37 | 具有 INTERNET 权限的应用会获得 ACCESS_LOCAL_NETWORK 的隐式权限授予,从而能够保留访问权限。这是临时行为,一旦应用将目标 SDK 提升到 37,系统将默认阻止此行为 |
无需立即更改代码 |
按使用情形划分的 LNP 策略
投屏:对于媒体投屏功能,最合适且能保护隐私的策略是使用输出切换器。此方法允许系统代表用户处理本地网络发现和连接,从而无需应用请求
ACCESS_LOCAL_NETWORK权限。浏览器:处理错误需要根据协议采取不同的方法。UDP 错误会导致
EPERM错误代码。对于 TCP 连接,浏览器应使用 NDK APIandroid_getnetworkblockedreason(int sockFd)来确定数据包是否被 LNP 阻止,此 API 会返回ANDROID_NETWORK_BLOCKED_REASON_LNP。其他使用情形(例如,IoT):使用 mDNS 查找设备的应用应使用
android.net.nsd.DiscoveryRequest#FLAG_SHOW_PICKER,以便在没有相应权限的情况下查找设备,并使用NsdManager#registerServiceInfoCallback/NsdManager#resolveService获取 IP 地址。连接到以这种方式获得的 IP 地址不需要ACCESS_LOCAL_NETWORK权限。
对于需要直接进行本地网络通信且无法使用系统介导的选择器的应用,建议的方法是使用权限重置计数器策略。如果用户撤消了 ACCESS_LOCAL_NETWORK 权限,此机制会为应用重新请求权限提供更多机会,让开发者能够向用户提供更清晰的理由。
Android 16 指南
如需选择启用本地网络限制,请执行以下操作:
- 将设备刷写到搭载 Android 16 Beta 3 或更高版本的 build
- 安装要测试的应用
使用 adb 切换 Appcompat 配置
adb shell am compat enable RESTRICT_LOCAL_NETWORK <package_name>重新启动设备
现在,应用对本地网络的访问受到限制,任何访问本地网络的尝试都会导致套接字错误。
如果您使用的 API 在应用进程之外执行本地网络操作(例如 NsdManager),则在选择启用时,这些 API 不会受到影响。
如需恢复访问权限,您必须向应用授予 NEARBY_WIFI_DEVICES 权限。
- 确保应用在其
manifest中声明了NEARBY_WIFI_DEVICES权限。 - 依次前往设置 > 应用 > [应用名称] > 权限 > 附近的设备 > 允许
现在,您的应用对本地网络的访问权限应该已恢复,并且所有场景都应像选择启用应用之前一样正常运行。以下是应用网络流量受到的影响。
| 权限 | 出站 LAN 请求 | 出站/入站互联网请求 | 入站 LAN 请求 |
|---|---|---|---|
| 已授予 | Works | Works | Works |
| 未授予 | 最差排行榜 | Works | 最差排行榜 |
使用以下命令切换关闭 Appcompat 配置
adb shell am compat disable RESTRICT_LOCAL_NETWORK <package_name>
错误
如果本地网络访问权限请求因缺少权限而失败,请执行以下操作:
TCP 连接通常会导致超时错误。
UDP 错误和一般权限拒绝通常会导致 EPERM 错误代码
错误
提交 bug 和反馈:
- 局域网访问权限存在差异(您认为某些访问权限不应被视为“本地网络”访问权限)
- 应禁止但未禁止 LAN 访问的 bug
- 本应不被阻止但被阻止的 LAN 访问权限的 bug
以下内容应不会受到此变更的影响:
- 访问互联网
- 移动网络