Android 10 包含可能影响您应用的行为变更。此页面上列出的变更适用于在 Android 10 上运行的应用,无论应用的 targetSdkVersion
如何。您应测试您的应用并根据需要进行修改,以正确支持这些变更。
如果您的应用的 targetSdkVersion 为 29
或更高,您还需要支持其他变更。请务必阅读 面向 API 级别 29 应用的行为变更,了解详细信息。
注意:除了此页面上列出的变更外,Android 10 还引入了大量基于隐私的变更和限制,包括以下内容:
- 后台访问设备位置
- 后台活动启动
- 联系人关联信息
- MAC 地址随机化
- 相机元数据
- 权限模型
这些变更会影响所有应用,并增强用户隐私。要了解如何支持这些变更,请参阅 隐私变更 页面。
非 SDK 接口限制
为了确保应用的稳定性和兼容性,平台已开始限制您的应用在 Android 9(API 级别 28)中可以使用哪些 非 SDK 接口。Android 10 包含基于与 Android 开发者合作以及最新的内部测试而更新的受限非 SDK 接口列表。我们的目标是在限制非 SDK 接口之前确保提供公共替代方案。
如果您不会面向 Android 10(API 级别 29),这些变更中的一些可能不会立即影响您。但是,虽然您目前可以使用一些非 SDK 接口(取决于您的应用的目标 API 级别),但使用任何非 SDK 方法或字段总是存在使您的应用崩溃的高风险。
如果您不确定您的应用是否使用了非 SDK 接口,您可以 测试您的应用 以找出答案。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。但是,我们了解有些应用有使用非 SDK 接口的有效用例。如果您无法找到应用中某个功能的非 SDK 接口的替代方案,您应该 请求新的公共 API。
要了解更多信息,请参阅 Android 10 中对非 SDK 接口限制的更新 并参阅 非 SDK 接口限制。
手势导航
从 Android 10 开始,用户可以在设备上启用手势导航。如果用户启用了手势导航,则会影响设备上的所有应用,无论应用是否面向 API 级别 29。例如,如果用户从屏幕边缘向内滑动,系统会将该手势解释为返回导航,除非应用 专门覆盖屏幕部分的该手势。
要使您的应用与手势导航兼容,您需要将应用内容扩展到边缘到边缘,并适当地处理冲突的手势。有关信息,请参阅 手势导航 文档。
NDK
Android 10 包含以下 NDK 变更。
共享对象不能包含文本重定位
Android 6.0(API 级别 23)不允许 在共享对象中使用文本重定位。代码必须按原样加载,并且不得修改。此变更可改善应用加载时间和安全性。
SELinux 会对目标为 Android 10 或更高版本的应用强制执行此限制。如果这些应用继续使用包含文本重定位的共享对象,则它们存在崩溃的高风险。
对 Bionic 库和动态链接器路径的更改
从 Android 10 开始,多个路径是符号链接而不是常规文件。依赖于路径为常规文件的应用可能会崩溃。
/system/lib/libc.so
->/apex/com.android.runtime/lib/bionic/libc.so
/system/lib/libm.so
->/apex/com.android.runtime/lib/bionic/libm.so
/system/lib/libdl.so
->/apex/com.android.runtime/lib/bionic/libdl.so
/system/bin/linker
->/apex/com.android.runtime/bin/linker
这些更改也适用于文件的 64 位变体,其中 lib/
被替换为 lib64/
。
为了兼容性,符号链接在旧路径中提供。例如,/system/lib/libc.so
是指向 /apex/com.android.runtime/lib/bionic/libc.so
的符号链接。因此 dlopen(“/system/lib/libc.so”)
继续有效,但应用在实际尝试通过读取 /proc/self/maps
或类似内容来检查加载的库时会发现差异,这并不常见,但我们发现某些应用会将其作为其反黑客过程的一部分。如果是这样,则应将 /apex/…
路径添加为 Bionic 文件的有效路径。
映射到只执行内存的系统二进制文件/库
从 Android 10 开始,系统二进制文件和库的可执行段将被映射到内存中,并设置为仅执行(不可读),以此作为一种抵御代码重用攻击的加固技术。如果您的应用对标记为仅执行的内存段执行读取操作——无论是由于错误、漏洞还是故意进行内存检查——系统会向您的应用发送 SIGSEGV
信号。
您可以通过检查 /data/tombstones/
中相关的 tombstone 文件来确定此行为是否导致崩溃。与仅执行相关的崩溃包含以下中止消息:
Cause: execute-only (no-read) memory access error; likely due to data in .text.
为了解决此问题并执行内存检查等操作,可以通过调用 mprotect()
将仅执行段标记为读+执行。但是,我们强烈建议之后将其设置回仅执行,因为此访问权限设置可以更好地保护您的应用和用户。
安全
Android 10 包含以下安全更改。
默认启用 TLS 1.3
在 Android 10 及更高版本中,所有 TLS 连接默认启用 TLS 1.3。以下是关于我们的 TLS 1.3 实现的一些重要细节:
- 无法自定义 TLS 1.3 密码套件。启用 TLS 1.3 时,始终启用支持的 TLS 1.3 密码套件。任何尝试通过调用
setEnabledCipherSuites()
来禁用它们的尝试都会被忽略。 - 协商 TLS 1.3 时,
HandshakeCompletedListener
对象会在会话添加到会话缓存 *之前* 被调用。(在 TLS 1.2 和之前的版本中,这些对象会在会话添加到会话缓存 *之后* 被调用。) - 在某些情况下,
SSLEngine
实例在 Android 的先前版本中抛出SSLHandshakeException
,在 Android 10 及更高版本中,这些实例改为抛出SSLProtocolException
。 - 不支持 0-RTT 模式。
如果需要,您可以通过调用 SSLContext.getInstance("TLSv1.2")
获取已禁用 TLS 1.3 的 SSLContext
。您还可以通过在适当的对象上调用 setEnabledProtocols()
来根据每个连接启用或禁用协议版本。
使用 SHA-1 签名的证书在 TLS 中不受信任
在 Android 10 中,使用 SHA-1 哈希算法签名的证书在 TLS 连接中不受信任。自 2016 年以来,根 CA 就没有颁发此类证书,并且它们在 Chrome 或其他主要浏览器中也不再受信任。
如果连接的目标站点提供的证书使用了 SHA-1,则任何连接尝试都会失败。
KeyChain 行为更改和改进
某些浏览器(例如 Google Chrome)允许用户在 TLS 服务器发送证书请求消息作为 TLS 握手的一部分时选择证书。从 Android 10 开始,KeyChain
对象在调用 KeyChain.choosePrivateKeyAlias()
以显示用户的证书选择提示时,会遵守发行者和密钥规范参数。特别是,此提示不包含不符合服务器规范的选择。
如果没有可供用户选择的证书(例如,当没有证书与服务器规范匹配或设备未安装任何证书时),则根本不会显示证书选择提示。
此外,在 Android 10 或更高版本上,无需设备屏幕锁定即可将密钥或 CA 证书导入到 KeyChain
对象中。
其他 TLS 和加密更改
TLS 和加密库中有一些小的更改在 Android 10 上生效。
- AES/GCM/NoPadding 和 ChaCha20/Poly1305/NoPadding 密码从
getOutputSize()
返回更准确的缓冲区大小。 - 对于最大协议为 TLS 1.2 或更高的连接尝试,将省略
TLS_FALLBACK_SCSV
密码套件。由于 TLS 服务器实现的改进,我们不建议尝试 TLS 外部回退。相反,我们建议依赖 TLS 版本协商。 - ChaCha20-Poly1305 是 ChaCha20/Poly1305/NoPadding 的别名。
- 带有尾随点的主机名不被视为有效的 SNI 主机名。
- 在为证书响应选择签名密钥时,将遵守
CertificateRequest
中的 supported_signature_algorithms 扩展。 - 不透明签名密钥(例如来自 Android Keystore 的密钥)可以与 TLS 中的 RSA-PSS 签名一起使用。
Wi-Fi Direct 广播
在 Android 10 上,以下与 Wi-Fi Direct 相关的广播不是粘性的:
如果您的应用依赖于在注册时接收这些广播,因为它们是粘性的,请在初始化时使用适当的 get()
方法来获取信息。
Wi-Fi Aware 功能
Android 10 添加了支持,以便于使用 Wi-Fi Aware 数据路径创建 TCP/UDP 套接字。要创建连接到 ServerSocket
的 TCP/UDP 套接字,客户端设备需要知道服务器的 IPv6 地址和端口。以前需要通过带外通信来实现,例如使用 BT 或 Wi-Fi Aware 第 2 层消息传递,或者使用其他协议(例如 mDNS)在带内发现。使用 Android 10,可以在网络设置中进行通信。
服务器可以执行以下任一操作:
- 初始化
ServerSocket
并设置或获取要使用的端口。 - 将端口信息指定为 Wi-Fi Aware 网络请求的一部分。
以下代码示例显示如何将端口信息指定为网络请求的一部分:
Kotlin
val ss = ServerSocket() val ns = WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("some-password") .setPort(ss.localPort) .build() val myNetworkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(ns) .build()
Java
ServerSocket ss = new ServerSocket(); WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier .Builder(discoverySession, peerHandle) .setPskPassphrase(“some-password”) .setPort(ss.getLocalPort()) .build(); NetworkRequest myNetworkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(ns) .build();
然后,客户端执行 Wi-Fi Aware 网络请求以获取服务器提供的 IPv6 和端口。
Kotlin
val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { ... } override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) { ... } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { ... val ti = networkCapabilities.transportInfo if (ti is WifiAwareNetworkInfo) { val peerAddress = ti.peerIpv6Addr val peerPort = ti.port } } override fun onLost(network: Network) { ... } }; connMgr.requestNetwork(networkRequest, callback)
Java
callback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { ... } @Override public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { ... } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { ... TransportInfo ti = networkCapabilities.getTransportInfo(); if (ti instanceof WifiAwareNetworkInfo) { WifiAwareNetworkInfo info = (WifiAwareNetworkInfo) ti; Inet6Address peerAddress = info.getPeerIpv6Addr(); int peerPort = info.getPort(); } } @Override public void onLost(Network network) { ... } }; connMgr.requestNetwork(networkRequest, callback);
Go 设备上的 SYSTEM_ALERT_WINDOW
在 Android 10(Go 版本)设备上运行的应用无法接收 SYSTEM_ALERT_WINDOW
权限。这是因为绘制叠加窗口会消耗过多的内存,这对低内存 Android 设备的性能尤其有害。
如果在 Android 9 或更低版本上运行的 Go 版本设备上的应用接收了 SYSTEM_ALERT_WINDOW
权限,即使设备升级到 Android 10,该应用也会保留该权限。但是,在设备升级后,尚未拥有该权限的应用无法获得该权限。
如果 Go 设备上的应用发送带有操作 ACTION_MANAGE_OVERLAY_PERMISSION
的意图,系统会自动拒绝请求,并将用户带到一个 **设置** 屏幕,该屏幕显示不允许该权限,因为它会降低设备速度。如果 Go 设备上的应用调用 Settings.canDrawOverlays()
,则该方法始终返回 false。同样,这些限制不适用于在设备升级到 Android 10 之前已获得 SYSTEM_ALERT_WINDOW
权限的应用。
针对较旧 Android 版本的应用的警告
运行 Android 10 或更高版本的设备会在第一次运行任何目标 Android 5.1(API 级别 22)或更低版本的应用时警告用户。如果应用需要用户授予权限,则用户还可以有机会在应用第一次运行之前调整应用的权限。
由于 Google Play 的 目标 API 要求,只有在用户运行最近未更新的应用时,才会看到这些警告。对于通过其他商店分发的应用,类似的目标 API 要求在 2019 年正在生效。有关这些要求的更多信息,请参阅 2019 年扩展目标 API 级别要求。
已删除 SHA-2 CBC 密码套件
以下 SHA-2 CBC 密码套件已从平台中删除:
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
这些密码套件不如使用 GCM 的类似密码套件安全,大多数服务器都支持这些密码套件的 GCM 和 CBC 变体,或者都不支持。
应用使用情况
Android 10 引入了以下与应用使用情况相关的行为更改:
UsageStats 应用使用情况改进 - 当在分屏或画中画模式下使用应用时,Android 10 可以使用 UsageStats 精确跟踪应用使用情况。此外,Android 10 正确跟踪即时应用的使用情况。
每个应用的灰度 - Android 10 可以为每个应用设置灰度显示模式。
每个应用的干扰状态 - Android 10 可以选择性地将应用设置为“干扰状态”,在这种状态下,它们的通知会被抑制,并且不会显示为推荐应用。
暂停和播放 - 在 Android 10 中,暂停的应用无法播放音频。
HTTPS 连接更改
如果在 Android 10 上运行的应用将 null
传递给 setSSLSocketFactory()
,则会发生 IllegalArgumentException
。在以前的版本中,将 null
传递给 setSSLSocketFactory()
的效果与传递当前的 默认工厂 相同。
android.preference 库已弃用
从 Android 10 开始,android.preference
库已弃用。开发人员应改用 AndroidX preference 库,它是 Android Jetpack 的一部分。有关迁移和开发的更多资源,请查看更新的 设置指南 以及我们的 公共示例应用 和 参考文档。
ZIP 文件实用程序库变更
Android 10 对处理 ZIP 文件的 java.util.zip
包中的类进行了以下更改。这些更改使该库在 Android 和其他使用 java.util.zip
的平台之间的行为更加一致。
Inflater
在之前的版本中,如果在调用 end()
后调用 Inflater
类中的某些方法,则这些方法会抛出 IllegalStateException
。在 Android 10 中,这些方法改为抛出 NullPointerException
。
ZipFile
在 Android 10 及更高版本中,采用 File
、int
和 Charset
类型参数的 ZipFile
构造函数 如果提供的 ZIP 文件不包含任何文件,则不会抛出 ZipException
。
ZipOutputStream
在 Android 10 及更高版本中,ZipOutputStream
中的 finish()
方法如果尝试为不包含任何文件的 ZIP 文件写入输出流,则不会抛出 ZipException
。
相机变更
许多使用相机的应用程序假设,如果设备处于纵向配置,则物理设备也处于纵向方向,如 相机方向 中所述。过去这是一个安全的假设,但随着可折叠设备等可用设备类型的扩展,这种情况发生了变化。在这些设备上,此假设可能导致相机取景器显示错误旋转或缩放(或两者兼有)。
目标 API 级别为 24 或更高的应用程序应显式设置 android:resizeableActivity
并提供处理多窗口操作的必要功能。
电池使用情况跟踪
从 Android 10 开始,SystemHealthManager
在发生 *重大充电事件* 后设备拔下电源时会重置其电池使用情况统计信息。总的来说,重大充电事件是指:设备已充满电,或设备从几乎耗尽电量变为几乎充满电。
在 Android 10 之前,无论电池电量变化有多小,只要设备拔下电源,电池使用情况统计信息就会重置。
Android Beam 已弃用
在 Android 10 中,我们正式弃用了 Android Beam,这是一个较旧的功能,用于通过近场通信 (NFC) 在设备之间启动数据共享。我们还弃用了几个相关的 NFC API。Android Beam 仍可供希望使用它的设备制造商合作伙伴选择使用,但它不再处于积极开发中。但是,Android 将继续支持其他 NFC 功能和 API,并且读取标签和支付等用例将继续按预期工作。
java.math.BigDecimal.stripTrailingZeros() 行为变更
BigDecimal.stripTrailingZeros()
不再将尾随零作为特殊情况保留,如果输入值为零。
java.util.regex.Matcher 和 Pattern 行为变更
split()
的结果已更改,当输入开头存在零宽度匹配时,不再以空 String
("") 开头。这也影响 String.split()
。例如,"x".split("")
现在返回 {"x"}
,而它在旧版本的 Android 上返回 {"", "x"}
。"aardvark".split("(?=a)"
现在返回 {"a", "ardv", "ark"}
而不是 {"", "a", "ardv", "ark"}
。
无效参数的异常行为也得到了改进
appendReplacement(StringBuffer, String)
现在抛出IllegalArgumentException
而不是IndexOutOfBoundsException
,如果替换String
以单个反斜杠结尾,这是非法的。如果替换String
以$
结尾,现在也会抛出相同的异常。以前,在这种情况下不会抛出异常。replaceFirst(null)
如果抛出NullPointerException
,则不再在Matcher
上调用reset()
。NullPointerException
现在也将在没有匹配项时抛出。以前,只有在有匹配项时才会抛出。start(int group)
、end(int group)
和group(int group)
如果组索引超出范围,现在会抛出更通用的IndexOutOfBoundsException
。以前,这些方法会抛出ArrayIndexOutOfBoundsException
。
GradientDrawable 的默认角度现在为 TOP_BOTTOM
在 Android 10 中,如果您在 XML 中定义 GradientDrawable
并且没有提供角度测量值,则渐变方向默认为 TOP_BOTTOM
。这与之前的 Android 版本不同,之前的版本默认为 LEFT_RIGHT
。
作为解决方法,如果您更新到最新版本的 AAPT2,如果未指定角度测量值,则该工具会为旧版应用程序设置 0 的角度测量值。
使用默认 SUID 记录序列化的对象
从 Android 7.0(API 级别 24)开始,平台对可序列化对象的默认 serialVersionUID
进行了一个 修复。此修复不会影响目标 API 级别为 23 或更低的应用程序。
从 Android 10 开始,如果应用程序的目标 API 级别为 23 或更低,并且依赖于旧的、不正确的默认 serialVersionUID
,则系统会记录警告并建议代码修复。
具体来说,如果满足以下所有条件,系统会记录警告
- 应用程序的目标 API 级别为 23 或更低。
- 序列化一个类。
- 序列化的类使用默认的
serialVersionUID
,而不是显式设置serialVersionUID
。 - 默认的
serialVersionUID
与应用程序目标 API 级别为 24 或更高的serialVersionUID
不同。
此警告针对每个受影响的类记录一次。警告消息包含建议的修复,即显式将 serialVersionUID
设置为如果应用程序的目标 API 级别为 24 或更高则计算出的默认值。通过使用此修复,您可以确保如果某个类的对象在目标 API 级别为 23 或更低的应用程序上序列化,则该对象将被目标 API 级别为 24 或更高的应用程序正确读取,反之亦然。
java.io.FileChannel.map() 变更
从 Android 10 开始,FileChannel.map()
不支持非标准文件,例如 /dev/zero
,其大小无法使用 truncate()
更改。以前的 Android 版本会忽略 truncate()
返回的 errno,但 Android 10 会抛出 IOException。如果您需要旧的行为,则必须使用原生代码。