本地网络权限

任何具有 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)不需要本地网络访问权限。
  • 使用 Output Switcher 作为应用内选择器的应用不需要本地网络权限(更多指南将在后续版本中提供)。

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,系统就会默认屏蔽该权限 无需立即更改代码。在以 SDK 37 为目标平台之前,请勿在运行时请求 ACCESS_LOCAL_NETWORK

按使用场景划分的 LNP 策略

  • 投放:对于媒体投放功能,最合适且 可保护隐私的策略是使用输出切换器。此方法 允许系统代表用户处理本地网络发现和连接,从而无需应用请求 ACCESS_LOCAL_NETWORK 权限。

  • 浏览器 :处理错误需要根据协议采用不同的方法。UDP 错误会导致 EPERM 错误代码。对于 TCP 连接,浏览器应使用 NDK API android_getnetworkblockedreason(int sockFd) 来确定数据包是否被 LNP 屏蔽;此 API 会返回 ANDROID_NETWORK_BLOCKED_REASON_LNP

  • 其他使用场景(例如物联网) :使用 mDNS 查找设备的应用应使用 android.net.nsd.DiscoveryRequest#FLAG_SHOW_PICKER(允许在没有权限的情况下查找设备)和 NsdManager#registerServiceInfoCallback / NsdManager#resolveService 来获取 IP 地址。以这种方式获取的 IP 地址的连接不需要 ACCESS_LOCAL_NETWORK 权限。

对于需要直接进行本地网络通信且无法使用系统介导的选择器的应用,建议在以 Android 17 (SDK 37) 或更高版本为目标平台时使用权限重置计数器策略。如果用户撤消了 ACCESS_LOCAL_NETWORK权限,此机制 会为应用重新请求该权限提供更多机会, 从而让开发者能够向用户说明更清晰的理由。

Android 16 指南

如需选择启用本地网络限制,请执行以下操作:

  1. 将设备刷写为搭载 Android 16 Beta 3 或更高版本的 build
  2. 安装要测试的应用
  3. 使用 adb 切换 Appcompat 配置

    adb shell am compat enable RESTRICT_LOCAL_NETWORK <package_name>
    
  4. 重启设备

现在,您的应用对本地网络的访问受到限制,任何访问本地网络的尝试都会导致套接字错误。 如果您使用的是在应用进程之外执行本地网络操作的 API(例如 NsdManager),则在选择启用期间不会受到影响。

如需恢复访问权限,您必须向应用授予 NEARBY_WIFI_DEVICES 权限。

  • 确保应用在其 manifest 中声明了 NEARBY_WIFI_DEVICES 权限。
  • 依次前往设置 > 应用 > [应用名称] > 权限 > 附近的设备 > 允许

现在,您的应用对本地网络的访问权限应该已恢复,并且所有场景都应该像在选择启用应用之前一样正常运行。以下是应用网络流量受到的影响。

权限 出站 LAN 请求 出站/入站互联网请求 入站 LAN 请求
已授予 正常运行 正常运行 正常运行
未授予 失败 正常运行 失败

使用以下命令关闭 Appcompat 配置

adb shell am compat disable RESTRICT_LOCAL_NETWORK <package_name>

错误

如果本地网络访问请求因缺少权限而失败:

  • TCP 连接通常会导致超时错误

  • UDP 错误和一般权限拒绝通常会导致 EPERM 错误代码

bug

提交 bug 和以下方面的反馈:

  • LAN 访问权限存在差异(您认为某些访问权限不应被视为“本地网络”访问权限)
  • LAN 访问权限应被屏蔽但未被屏蔽的 bug
  • LAN 访问权限不应被屏蔽但被屏蔽的 bug

以下内容不应受到此变更的影响:

  • 连接到互联网
  • 移动网络