行为变更:所有应用

Android 9(API 级别 28)对 Android 系统引入了多项更改。以下行为变更将影响在 Android 9 平台上运行的所有应用,无论这些应用以哪个 API 级别为目标平台。所有开发者都应查看这些变更,并修改其应用以正确支持这些变更(如果适用)。

如需了解仅影响以 API 级别 28 或更高级别为目标的应用的变更,请参阅行为变更:以 API 级别 28 及更高级别为目标的应用

电源管理

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

有关详情,请参阅电源管理

隐私权变更

为了加强用户隐私保护,Android 9 引入了多项行为变更,例如限制后台应用对设备传感器的访问权限、限制通过 Wi-Fi 扫描检索到的信息,以及与通话、手机状态和 Wi-Fi 扫描相关的新权限规则和权限组。

无论采用哪种目标 SDK 版本,这些变更都会影响在 Android 9 上运行的所有应用。

限制后台传感器使用权限

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 提供的电话号码字段将为空。

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

限制了对 WLAN 位置信息和连接信息的访问

在 Android 9 中,应用执行 Wi-Fi 扫描的权限要求比以前的版本更严格。如需了解详情,请参阅 Wi-Fi 扫描限制

类似的限制也适用于 getConnectionInfo() 方法,该方法会返回描述当前 Wi-Fi 连接的 WifiInfo 对象。仅当发起调用的应用具有以下权限时,才能使用此对象的方法来检索 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 接口的限制

为了帮助确保应用的稳定性和兼容性,Android 平台会限制某些非 SDK 方法和字段的使用;无论您尝试直接访问这些方法和字段、通过反射还是使用 JNI 方法,这些限制都适用。在 Android 9 中,您的应用可以继续访问这些受限接口;该平台使用消息框和日志条目来提醒您。如果您的应用显示此类消息框,请务必采用受限接口之外的其他实现策略。如果您认为没有其他策略可行,可以提交 bug,请求重新审核限制。

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

安全行为变更

设备安全性变更

无论应用的目标平台版本如何,Android 9 添加了多项功能来提升应用的安全性。

TLS 实现变更

系统的 TLS 实现在 Android 9 中经过了多次变更:

如需详细了解如何在 Android 应用中发出安全的 Web 请求,请参阅 HTTPS 示例

更严格的 SECCOMP 过滤条件

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

加密变更

Android 9 针对加密算法的实现和处理引入了几项变更。

参数和算法的 Conscrypt 实现

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

如果您的应用以 Android 8.1(API 级别 27)或更低版本为目标平台,那么在请求这些已废弃算法之一的 Bouncy Castle 实现时,您会收到一条警告。不过,如果您以 Android 9 为目标平台,则这些请求会各自抛出 NoSuchAlgorithmException

其他变更

Android 9 引入了多项与加密有关的其他变更:

  • 使用 PBE 密钥时,如果 Bouncy Castle 需要初始化矢量 (IV),而您的应用未提供 IV,则会收到一条警告。
  • ARC4 加密的 Conscrypt 实现允许您指定 ARC4/ECB/NoPaddingARC4/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)中,Android 平台引入了一种可合并的存储设备技术,开发者可以使用该技术代替 ASEC。

ICU 库更新

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

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

ICU 60 的更新包含许多细微但实用的更改,包括表情符号 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)的字符串。
    • 亚洲/河内不再是可识别的时区。因此,java.util.TimeZones.getAvailableIds() 不会返回此值,java.util.TimeZone.getTimeZone() 也无法识别该值。此行为与现有的 android.icu 行为一致。
  • 即使在解析合法货币文本时,android.icu.text.NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String) 方法也可能会抛出 ParseException。通过对 PLURALCURRENCYSTYLE 样式的货币文本使用 NumberFormat.parseCurrency(从 Android 7.0(API 级别 24)开始提供)来避免此问题。

Android Test 变更

Android 9 针对 Android Test 框架的库和类结构引入了几项更改。这些变更可帮助开发者使用由框架支持的公共 API,但也在使用第三方库或自定义逻辑构建和运行测试时提供了更大的灵活性。

从框架中移除的库

Android 9 将基于 JUnit 的类重新整理成三个库:android.test.baseandroid.test.runnerandroid.test.mock。此变更允许您针对最适合项目依赖项的 JUnit 版本运行测试。此版本的 JUnit 可能与 android.jar 提供的版本不同。

如需详细了解如何将基于 JUnit 的类整理到这些库中,以及如何准备应用项目以编写和运行测试,请参阅针对 Android Test 设置项目

测试套件 build 变更

移除了 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() 方法对套接字进行标记,则当使用包含 ParcelFileDescriptor 容器的 binder IPC 将其发送到另一个进程时,该套接字会被取消标记。

在 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 设置,会有一些例外情况(见下表)。

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

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

旋转锁定模式通过设置 WindowManager 在处理 activity 旋转时使用的用户旋转偏好设置来发挥作用。在以下情况下,用户旋转偏好设置可能会发生变化。请注意,恢复到设备的自然旋转是存在偏差的,对于手机外形规格的设备,自然旋转通常是纵向:

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

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

屏幕方向 行为
未指定,用户 在自动屏幕旋转和旋转锁定下,activity 可以纵向或横向(以及反向)呈现。预期同时支持纵向和横向布局。
用户横向 在自动屏幕旋转和旋转锁定下,activity 可以横向或反向横向呈现。预期只支持横向布局。
用户肖像 在自动屏幕旋转和旋转锁定下,activity 可以纵向或反向纵向呈现。预期只支持纵向布局。
完整权限 在自动屏幕旋转和旋转锁定下,activity 可以纵向或横向(以及反向)呈现。应同时支持纵向和横向布局。

旋转锁定用户将可以选择锁定为反向纵向,通常为 180o。
sensor、fullSensor、sensorPortrait、sensorLandscape 系统会忽略旋转锁定模式偏好设置,并将其视为自动屏幕旋转已启用。请仅在例外情况下并经过仔细的用户体验考量后再使用此项。

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() 来发现每个可用的摄像头。应用不应假设设备只有一个后置摄像头或只有一个前置摄像头。

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