行为变更:所有应用

Android 9(API 级别 28)对 Android 系统进行了一些更改。以下行为更改适用于在 Android 9 平台上运行的所有应用,无论它们的目标 API 级别是什么。所有开发人员都应查看这些更改,并在适用的情况下修改其应用以正确支持它们。

有关仅影响目标 API 级别为 28 或更高版本的应用的更改,请参阅行为变更:面向 API 级别 28 及更高版本的应用

电源管理

Android 9 引入了新的功能来改进设备电源管理。这些更改以及 Android 9 之前已存在的功能有助于确保系统资源可供最需要的应用使用。

有关详细信息,请参阅电源管理

隐私更改

为了增强用户隐私,Android 9 引入了多项行为更改,例如限制后台应用访问设备传感器、限制从 Wi-Fi 扫描检索的信息,以及与电话呼叫、电话状态和 Wi-Fi 扫描相关的新的权限规则和权限组。

这些更改会影响在 Android 9 上运行的所有应用,无论目标 SDK 版本如何。

限制后台访问传感器

Android 9 限制了后台应用访问用户输入和传感器数据的能力。如果您的应用在运行 Android 9 的设备上后台运行,系统会对您的应用应用以下限制

  • 您的应用无法访问麦克风或摄像头。
  • 使用连续报告模式的传感器(例如,加速度计和陀螺仪)不会接收事件。
  • 使用更改时一次性报告模式的传感器不会接收事件。

如果您的应用需要检测运行 Android 9 的设备上的传感器事件,请使用前台服务

限制访问通话记录

Android 9 引入了CALL_LOG权限组,并将READ_CALL_LOGWRITE_CALL_LOGPROCESS_OUTGOING_CALLS权限移动到此组。在以前的 Android 版本中,这些权限位于PHONE权限组中。

CALL_LOG权限组使用户能够更好地控制和查看需要访问有关电话呼叫的敏感信息的应用,例如读取电话呼叫记录和识别电话号码。

如果您的应用需要访问通话记录或需要处理拨出的电话,则必须从CALL_LOG权限组中显式请求这些权限。否则,会发生SecurityException

注意:由于这些权限已更改组并在运行时授予,因此用户可能会拒绝您的应用访问电话呼叫日志信息。在这种情况下,您的应用应能够优雅地处理缺少访问信息的状况。

如果您的应用已遵循运行时权限最佳实践,则可以处理权限组的更改。

限制访问电话号码

在 Android 9 上运行的应用在获取READ_CALL_LOG权限(以及应用用例所需的其他权限)之前,无法读取电话号码或电话状态。

与传入和传出呼叫关联的电话号码在电话状态广播中可见,例如传入和传出呼叫,并且可以通过PhoneStateListener类访问。但是,如果没有READ_CALL_LOG权限,则PHONE_STATE_CHANGED广播和PhoneStateListener提供的电话号码字段将为空。

要从电话状态读取电话号码,请根据您的用例更新您的应用以请求必要的权限。

限制对 Wi-Fi 位置和连接信息的访问

在 Android 9 中,应用执行 Wi-Fi 扫描的权限要求比以前版本更严格。有关详细信息,请参阅Wi-Fi 扫描限制

类似的限制也适用于getConnectionInfo()方法,该方法返回一个WifiInfo对象,描述当前的 Wi-Fi 连接。只有在调用应用具有以下权限时,才能使用此对象的方法检索 SSID 和 BSSID 值。

  • ACCESS_FINE_LOCATION **或** ACCESS_COARSE_LOCATION
  • ACCESS_WIFI_STATE

检索 SSID 或 BSSID 还需要在设备上启用位置服务(在**设置 > 位置**下)。

从 Wi-Fi 服务方法中删除的信息

在 Android 9 中,以下事件和广播不会接收有关用户位置或个人身份信息的信息。

来自 Wi-Fi 的NETWORK_STATE_CHANGED_ACTION系统广播不再包含 SSID(以前为 EXTRA_SSID)、BSSID(以前为 EXTRA_BSSID)或连接信息(以前为 EXTRA_NETWORK_INFO)。如果您的应用需要此信息,请改为调用getConnectionInfo()

电话信息现在依赖于设备位置设置

如果用户在运行 Android 9 的设备上禁用了设备位置,则以下方法不会提供结果。

使用非 SDK 接口的限制

为了帮助确保应用的稳定性和兼容性,平台限制了一些非 SDK 方法和字段的使用;无论您是直接访问这些方法和字段,还是通过反射或使用 JNI 访问,这些限制都适用。在 Android 9 中,您的应用可以继续访问这些受限制的接口;平台使用吐司和日志条目来提请您注意。如果您的应用显示了这样的吐司,那么您必须寻求除受限制接口之外的其他实现策略非常重要。如果您认为没有可行的替代策略,您可以提交错误以请求重新考虑限制。

非 SDK 接口的限制包含更多重要信息。您应该查看它以确保您的应用继续正常运行。

安全行为更改

设备安全更改

Android 9 添加了一些功能,可以提高您应用的安全级别,无论您的应用目标哪个版本。

TLS 实现更改

Android 9 中系统的 TLS 实现经历了一些更改。

要了解有关在 Android 应用中发出安全 Web 请求的更多信息,请参阅HTTPS 示例

更严格的 SECCOMP 过滤器

Android 9 进一步限制了应用可用的系统调用。此行为是Android 8.0(API 级别 26)包含的 SECCOMP 过滤器的扩展。

加密更改

Android 9 对加密算法的实现和处理引入了多项更改。

Conscrypt 对参数和算法的实现

Android 9 在Conscrypt中提供了算法参数的其他实现。这些参数包括:AES、DESEDE、OAEP 和 EC。Bouncy Castle版本的这些参数和许多算法从 Android 9 开始已弃用。

如果您的应用的目标是 Android 8.1(API 级别 27)或更低版本,则在请求这些已弃用算法的 Bouncy Castle 实现时,您会收到警告。但是,如果您将目标设置为 Android 9,则这些请求都会抛出一个NoSuchAlgorithmException

其他更改

Android 9 引入了其他一些与加密相关的更改。

  • 使用 PBE 密钥时,如果 Bouncy Castle 正在期望初始化向量 (IV) 并且您的应用没有提供一个,则您会收到警告。
  • Conscrypt 对 ARC4 密码的实现允许您指定**ARC4/ECB/NoPadding**或**ARC4/NONE/NoPadding**。
  • Crypto Java 加密体系结构 (JCA) 提供程序已被删除。因此,如果您的应用调用SecureRandom.getInstance("SHA1PRNG", "Crypto"),则会发生NoSuchProviderException
  • 如果您的应用从大于密钥结构的缓冲区解析 RSA 密钥,则不再发生异常。

要了解有关使用 Android 加密功能的更多信息,请参阅加密

Android 安全加密文件不再受支持

Android 9 完全删除了对 Android 安全加密文件 (ASEC) 的支持。

在 Android 2.2(API 级别 8)中,Android 引入了 ASEC 以支持 SD 卡上的应用功能。在 Android 6.0(API 级别 23)中,平台引入了可采用存储设备技术,开发人员可以使用它来代替 ASEC。

ICU 库的更新

Android 9 使用 ICU 库的版本 60。Android 8.0(API 级别 26)和 Android 8.1(API 级别 27)使用 ICU 58。

ICU 用于在android.icu 包下提供公共 API,并在 Android 平台内部用于国际化支持。例如,它用于实现java.utiljava.textandroid.text.format中的 Android 类。

更新到 ICU 60 包含许多小的但有用的更改,包括 Emoji 5.0 数据支持和改进的日期/时间格式,如 ICU 59 和 ICU 60 发行说明中所述。

此更新中的显著变化

  • 平台处理时区的方式发生了变化。
    • 平台更好地处理 GMT 和 UTC;UTC 不再是 GMT 的同义词。

      ICU 现在为 GMT 和 UTC 提供翻译后的时区名称。此更改会影响“GMT”、“Etc/GMT”、“UTC”、“Etc/UTC”和“Zulu”等时区的android.icu格式化和解析行为。

    • java.text.SimpleDateFormat现在使用 ICU 为 UTC/GMT 提供显示名称,这意味着
      • 格式化zzzz会为许多语言环境生成一个长的本地化字符串。以前,它为 UTC 生成“UTC”,为 GMT 生成“GMT+00:00”之类的字符串。
      • 解析zzzz识别“协调世界时”和“格林威治标准时间”之类的字符串。
      • 如果应用假设所有语言的zzzz输出都是“UTC”或“GMT+00:00”,则可能会遇到兼容性问题。
    • java.text.DateFormatSymbols.getZoneStrings()的行为已更改。
      • SimpleDateFormat一样,UTC 和 GMT 现在具有长名称。UTC 时区的时区名称的 DST 变体(如“UTC”、“Etc/UTC”和“Zulu”)将变为 GMT+00:00,这是在没有可用名称时的标准回退,而不是硬编码字符串UTC
      • 一些时区 ID 被正确识别为其他时区的同义词,以便 Android 找到以前无法解析的过时时区 ID(如Eire)的字符串。
    • Asia/Hanoi 不再是公认的时区。因此,java.util.TimeZones.getAvailableIds()不会返回此值,并且java.util.TimeZone.getTimeZone()不会识别它。此行为与现有的android.icu行为一致。
  • android.icu.text.NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String)方法即使在解析合法的货币文本时也可能会抛出ParseException。通过使用自 Android 7.0(API 级别 24)起可用的NumberFormat.parseCurrency来避免此问题,以用于PLURALCURRENCYSTYLE样式的货币文本。

Android 测试更改

Android 9 对 Android 测试框架的库和类结构进行了一些更改。这些更改有助于开发人员使用框架支持的公共 API,但也允许在使用第三方库或自定义逻辑构建和运行测试方面具有更大的灵活性。

从框架中删除的库

Android 9 将基于 JUnit 的类重新组织到三个库中:**android.test.base**、**android.test.runner**和**android.test.mock**。此更改允许您针对最适合您的项目依赖项的 JUnit 版本运行测试。此版本的 JUnit 可能与android.jar提供的版本不同。

要了解有关基于 JUnit 的类如何组织到这些库中以及如何为您的应用项目准备编写和运行测试的更多信息,请参阅为 Android 测试设置项目

测试套件构建更改

TestSuiteBuilder类中的addRequirements()方法已被删除,并且TestSuiteBuilder类本身已弃用。addRequirements()方法要求开发人员提供类型为隐藏 API 的参数,从而使 API 无效。

Java UTF 解码器

UTF-8 是 Android 中的默认字符集。UTF-8 字节序列可以通过String构造函数解码,例如String(byte[] bytes)

Android 9 中的 UTF-8 解码器比以前版本更严格地遵循 Unicode 标准。更改包括以下内容:

  • UTF-8 的非最短形式,例如 <C0, AF>,会被视为非法。
  • UTF-8 的代理形式,例如 U+D800..U+DFFF,会被视为非法。
  • 最大子部分将被替换为单个 U+FFFD。例如,在字节序列 "41 C0 AF 41 F4 80 80 41" 中,最大子部分为 "C0"、"AF" 和 "F4 80 80"。"F4 80 80" 可以是 "F4 80 80 80" 的初始子序列,但 "C0" 不能是任何合法代码单元序列的初始子序列。因此,输出应为 "A\ufffd\ufffdA\ufffdA"。
  • 要在 Android 9 或更高版本中解码修改后的 UTF-8/CESU-8 序列,请使用 DataInputStream.readUTF() 方法或 NewStringUTF() JNI 方法。

使用证书进行主机名验证

RFC 2818 描述了两种将域名与证书匹配的方法——使用 subjectAltName (SAN) 扩展名中的可用名称,或者在没有 SAN 扩展名的情况下,回退到 commonName (CN)。

但是,RFC 2818 已弃用回退到 CN 的做法。因此,Android 不再回退到使用 CN。要验证主机名,服务器必须提供具有匹配 SAN 的证书。不包含与主机名匹配的 SAN 的证书不再受信任。

网络地址查找可能导致网络违规

需要名称解析的网络地址查找可能涉及网络 I/O,因此被视为阻塞操作。主线程上的阻塞操作可能导致暂停或卡顿。

StrictMode 类是一个开发工具,可帮助开发人员检测代码中的问题。

在 Android 9 及更高版本中,StrictMode 会检测由需要名称解析的网络地址查找引起的网络违规。

您不应在启用了 StrictMode 的情况下发布您的应用。如果这样做,您的应用可能会遇到异常,例如使用 detectNetwork()detectAll() 方法获取检测网络违规的策略时出现的 NetworkOnMainThreadException

解析数字 IP 地址不被视为阻塞操作。数字 IP 地址解析的工作方式与 Android 9 之前的版本相同。

套接字标记

在低于 Android 9 的平台版本中,如果使用 setThreadStatsTag() 方法标记套接字,则当使用 Binder IPCParcelFileDescriptor 容器将其发送到另一个进程时,套接字的标记将被取消。

在 Android 9 及更高版本中,当使用 Binder IPC 将套接字发送到另一个进程时,套接字标记将被保留。此更改可能会影响网络流量统计信息,例如,当使用 queryDetailsForUidTag() 方法时。

如果您希望保留先前版本的行为(即取消发送到另一个进程的套接字的标记),可以在发送套接字之前调用 untagSocket()

套接字中可用字节数的报告数量

在调用 shutdownInput() 方法后调用 available() 方法时,它将返回 0

更详细的 VPN 网络功能报告

在 Android 8.1(API 级别 27)及更低版本中,NetworkCapabilities 类仅报告了 VPN 的有限信息集,例如 TRANSPORT_VPN,但省略了 NET_CAPABILITY_NOT_VPN。此有限信息使得难以确定使用 VPN 是否会导致向应用的用户收费。例如,检查 NET_CAPABILITY_NOT_METERED 将无法确定底层网络是否计费。

在 Android 9 及更高版本中,当 VPN 调用 setUnderlyingNetworks() 方法时,Android 系统会合并任何底层网络的传输和功能,并将结果作为 VPN 网络的有效网络功能返回。

在 Android 9 及更高版本中,已经检查 NET_CAPABILITY_NOT_METERED 的应用会接收 VPN 和底层网络的网络功能。

xt_qtaguid 文件夹中的文件不再对应用可用

从 Android 9 开始,应用不允许直接读取 /proc/net/xt_qtaguid 文件夹中的文件。这样做的原因是为了确保与某些根本没有这些文件的设备保持一致。

依赖于这些文件的公共 API,TrafficStatsNetworkStatsManager,将继续按预期工作。但是,不受支持的 cutils 函数(例如 qtaguid_tagSocket())在不同的设备上可能无法按预期工作,甚至根本无法工作。

现在强制执行 FLAG_ACTIVITY_NEW_TASK 要求

在 Android 9 中,除非您传递 Intent 标志 FLAG_ACTIVITY_NEW_TASK,否则您不能从非 Activity 上下文启动 Activity。如果您尝试在不传递此标志的情况下启动 Activity,则 Activity 不会启动,并且系统会将消息打印到日志中。

屏幕旋转更改

从 Android 9 开始,纵向旋转模式发生了重大变化。在 Android 8.0(API 级别 26)中,用户可以使用快速设置磁贴或显示设置在自动旋转纵向旋转模式之间切换。纵向模式已重命名为旋转锁定,并且在关闭自动旋转时处于活动状态。自动旋转模式没有任何更改。

当设备处于旋转锁定模式时,用户可以将其屏幕锁定到最顶层可见 Activity 支持的任何旋转。Activity 不应假设它将始终以纵向渲染。如果最顶层 Activity 可以在自动旋转模式下以多种旋转方式渲染,则在旋转锁定模式下也应提供相同的选项,但基于 Activity 的 screenOrientation 设置有一些例外情况(请参见下表)。

请求特定方向的 Activity(例如,screenOrientation=landscape)会忽略用户锁定偏好,并与 Android 8.0 中的行为相同。

可以在 Android 清单 中的 Activity 级别设置屏幕方向偏好,或者使用 setRequestedOrientation() 以编程方式设置。

旋转锁定模式通过设置用户旋转偏好来工作,WindowManager 在处理 Activity 旋转时会使用该偏好。在以下情况下可能会更改用户旋转偏好。请注意,有一种倾向是返回到设备的自然旋转,对于手机外形尺寸的设备,自然旋转通常是纵向

  • 当用户接受旋转建议时,旋转偏好会更改为该建议。
  • 当用户切换到强制纵向应用(包括锁定屏幕或启动器)时,旋转偏好会更改为纵向。

下表总结了常见屏幕方向的旋转行为

屏幕方向 行为
未指定,用户 在自动旋转和旋转锁定中,Activity 可以以纵向或横向渲染(反之亦然)。预期支持纵向和横向布局。
userLandscape 在自动旋转和旋转锁定中,Activity 可以以横向或反向横向渲染。预期仅支持横向布局。
userPortrait 在自动旋转和旋转锁定中,Activity 可以以纵向或反向纵向渲染。预期仅支持纵向布局。
fullUser 在自动旋转和旋转锁定中,Activity 可以以纵向或横向渲染(反之亦然)。预期支持纵向和横向布局。

旋转锁定用户将可以选择锁定到反向纵向,通常为 180º。
sensor、fullSensor、sensorPortrait、sensorLandscape 旋转锁定模式偏好将被忽略,并被视为自动旋转处于活动状态。仅在非常特殊的情况下且经过非常谨慎的 UX 考虑后才使用此设置。

Apache HTTP 客户端弃用会影响使用非标准 ClassLoader 的应用

在 Android 6.0 中,我们删除了对 Apache HTTP 客户端的支持。此更改对绝大多数不面向 Android 9 或更高版本的应用没有影响。但是,此更改可能会影响某些使用非标准 ClassLoader 结构的应用,即使这些应用不面向 Android 9 或更高版本。

如果应用使用非标准 ClassLoader 明确委托给系统 ClassLoader,则可能会受到影响。这些应用在查找 org.apache.http.* 中的类时,需要改为委托给应用 ClassLoader。如果它们委托给系统 ClassLoader,则这些应用将在 Android 9 或更高版本上失败,并出现 NoClassDefFoundError,因为系统 ClassLoader 不再识别这些类。为了防止将来出现类似问题,应用通常应通过应用 ClassLoader 加载类,而不是直接访问系统 ClassLoader

枚举摄像头

在 Android 9 设备上运行的应用可以通过调用 getCameraIdList() 来发现所有可用的摄像头。应用不应假设设备仅具有一个后置摄像头或仅具有一个前置摄像头。

例如,如果您的应用有一个按钮用于在前后摄像头之间切换,则可能有多个前置或后置摄像头可供选择。您应该遍历摄像头列表,检查每个摄像头的特性,并决定向用户公开哪些摄像头。