Android 9 行为变更:所有应用

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_LOG 和 PROCESS_OUTGOING_CALLS 权限移入该组。 在之前的 Android 版本中,这些权限位于 PHONE 权限组。

对于需要访问通话敏感信息(如读取通话记录和识别电话号码)的应用,该 CALL_LOG 权限组为用户提供了更好的控制和可见性。

如果您的应用需要访问通话记录或者需要处理去电,则您必须向 CALL_LOG 权限组明确请求这些权限。 否则会发生 SecurityException

注:因为这些权限已变更组并在运行时授予,用户可以拒绝您的应用访问通话记录信息。 在这种情况下,您的应用应该能够妥善处理无法访问信息的状况。

如果您的应用已经遵循运行时权限最佳做法,则可以处理权限组的变更。

限制访问电话号码

在未首先获得 READ_CALL_LOG 权限的情况下,除了应用的用例需要的其他权限之外,运行于 Android 9 上的应用无法读取电话号码或手机状态。

与来电和去电关联的电话号码可在手机状态广播(比如来电和去电的手机状态广播)中看到,并可通过 PhoneStateListener 类访问。 但是,如果没有 READ_CALL_LOG 权限,则 PHONE_STATE_CHANGED 广播和 PhoneStateListener 提供的电话号码字段为空。

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

  • 要通过 PHONE_STATE Intent 操作读取电话号码,同时需要 READ_CALL_LOG 权限和 READ_PHONE_STATE 权限。
  • 要从 onCallStateChanged() 中读取电话号码,只需要 READ_CALL_LOG 权限。 不需要 READ_PHONE_STATE 权限。

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

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

类似的限制也适用于 getConnectionInfo() 函数,该函数返回描述当前 Wi-Fi 连接的 WifiInfo 对象。 如果调用应用具有以下权限,则只能使用该对象的函数来检索 SSID 和 BSSID 值:

  • ACCESS_FINE_LOCATION  ACCESS_COARSE_LOCATION
  • ACCESS_WIFI_STATE

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

从 Wi-Fi 服务函数中移除的信息

在 Android 9 中,下列事件和广播不接收用户位置或个人可识别数据方面的信息:

  • WifiManager 中的 getScanResults() 和 getConnectionInfo() 函数。
  • WifiP2pManager 中的 discoverServices() 和 addServiceRequest() 函数。
  • NETWORK_STATE_CHANGED_ACTION 广播。

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

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

如果用户在运行 Android 9 的设备上停用设备定位,则以下函数不提供结果:

  • getAllCellInfo()
  • listen()
  • getCellLocation()
  • getNeighboringCellInfo()

对使用非 SDK 接口的限制

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

对非 SDK 接口的限制包含了更多重要信息。 您应查阅该信息以确保您的应用继续正常工作。

安全行为变更

设备安全性变更

无论应用的目标平台版本如何,Android 9 添加的若干功能均可令应用的安全性得到改善。

传输层安全协议 (TLS) 实现变更

系统的传输层安全协议 (TLS) 实现在 Android 9 中经历了若干次变更:

  • 如果 SSLSocket 的实例在创建时连接失败,系统会引发 IOException 而非 NullPointerException
  • SSLEngine 类可正常处理出现的任何 close_notify 提醒。

如需了解有关在 Android 应用中进行安全网络请求的更多信息,请参阅一个 HTTPS 示例。

更严格的 SECCOMP 过滤器

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

注:此更改仅影响使用授权的系统调用的应用。

加密变更

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

参数和算法的 Conscrypt 实现

Android 9 在 Conscrypt 中实现了更多的算法参数。 这些参数包括: AES、DESEDE、OAEP 和 EC。 这些参数和许多算法的 Bouncy Castle 版本自 Android 9 起已被弃用。

注:EC 参数的 Conscrypt 实现仅支持已命名的曲线。

如果您的应用以 Android 8.1(API 级别 27)或更低版本为目标,则在请求这些已弃用算法之一的 Bouncy Castle 实现时,您将收到一条警告消息。 然而,如果您以 Android 9 为目标平台,则这些请求会各自引发 NoSuchAlgorithmException

其他变更

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

  • 使用 PBE 密钥时,如果 Bouncy Castle 需要初始化矢量 (IV),而您的应用未提供 IV,则会收到一条警告消息。
  • ARC4 加密的 Conscrypt 实现允许您指定 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 package 下的公开 API, 供 Android 平台内部用来提供国际化支持。 例如,它用于实现 java.utiljava.text 和 android.text.format 格式的 Android 类。

对 ICU 60 进行的更新包含许多细微但很有用的变更,这包括 Emoji 5.0 数据支持,改进了日期/时间格式,详见 ICU 59 和 ICU 60 版本说明中的介绍。

本次更新中的显著变更:

  • 平台处理时区的方式已发生更改。
    • 平台能够更好地处理 GMT 和 UTC,不再将 UTC 与 GMT 混为一谈。

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

    • java.text.SimpleDateFormat 现在使用 ICU 提供 UTC /GMT 的显示名称,这意味着:
      • 对于许多语言区域而言,设置 zzzz 的格式将会生成很长的本地化字符串。之前,对于 UTC 时区,它会生成“UTC”,而对于 GMT,则会生成“GMT+00:00”之类的字符串。
      • 解析 zzzz 可识别“Universal Coordinated Time”和“Greenwich Mean Time”之类的字符串。
      • 在所有语言里,如果应用接受“UTC”或“GMT+00:00”作为 zzzz 的输出,则可能会遇到兼容性问题。
    • 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 类型的相应币种文本使用自 Android 7.0(API 级别 24)以来所提供的NumberFormat.parseCurrency,可避免此问题。

Android Test 变更

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

从框架移除的内容库

Android 9 将基于 JUnit 的类重新整理成三个内容库: android.test.baseandroid.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(byte[] bytes) 之类的 String 构造函数解码。

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 进程间通信将其发送给其他进程时,套接字会被取消标记。

在 Android 9 及更高版本中,利用 binder 进程间通信将套接字发送至其他进程时,其标记将得到保留。 此变更可能影响网络流量统计,例如,使用 queryDetailsForUidTag() 函数时。

如果您要保留以前版本的行为,即取消已发送至其他进程的套接字的标记,您可以在发送此套接字之前调用 untagSocket()

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

在调用 shutdownInput() 函数后,available() 函数会在调用时返回 0

更详尽的 VPN 网络功能报告

在 Android 8.1(API 级别 28)及更低版本中,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 TrafficStats 和 NetworkStatsManager 继续按照预期方式运行。 然而,不受支持的 cutils函数(例如 qtaguid_tagSocket())在不同设备上可能不会按照预期方式运行 — 甚至根本不运行。

现在强制执行 FLAG_ACTIVITY_NEW_TASK 要求

在 Android 9 中,您不能从非 Activity 环境中启动 Activity,除非您传递 Intent 标志 FLAG_ACTIVITY_NEW_TASK。 如果您尝试在不传递此标志的情况下启动 Activity,则该 Activity 不会启动,系统会在日志中输出一则消息。

注:在 Android 7.0(API 级别 24)之前,标志要求一直是期望的行为并被强制执行。 Android 7.0 中的一个错误会临时阻止实施标志要求。

屏幕旋转变更

从 Android 9 开始,对纵向旋转模式做出了重大变更。 在 Android 8.0(API 级别 26)中,用户可以使用 Quicksettings 图块或 Display 设置在自动屏幕旋转纵向旋转模式之间切换。 纵向模式已重命名为旋转锁定,它会在自动屏幕旋转关闭时启用。 自动屏幕旋转模式没有任何变更。

当设备处于旋转锁定模式时,用户可将其屏幕锁定到顶层可见 Activity 所支持的任何旋转。 Activity 不应假定它将始终以纵向呈现。 如果顶层 Activity 可在自动屏幕旋转模式下以多种旋转呈现,则应在旋转锁定模式下提供相同的选项,根据 Activity 的 screenOrientation 设置,允许存在一些例外情况(见下表)。

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

可在 Android Manifest 中,或以编程方式通过 setRequestedOrientation() 在 Activity 级别设置屏幕方向首选项。

旋转锁定模式通过设置 WindowManager 在处理 Activity 旋转时使用的用户旋转首选项来发挥作用。 用户旋转首选项可能在下列情况下发生变更。 请注意,恢复设备的自然旋转存在偏差,对于外形与手机类似的设备通常设置为纵向:

  • 当用户接受旋转建议时,旋转首选项变为建议方向。
  • 当用户切换到强制纵向应用(包括锁定屏幕或启动器)时,旋转首选项变为纵向。

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

屏幕方向 行为
未指定、user 在自动屏幕旋转和旋转锁定下,Activity 可以纵向或横向(以及颠倒纵向或横向)呈现。 预期同时支持纵向和横向布局。
userLandscape 在自动屏幕旋转和旋转锁定下,Activity 可以横向或颠倒横向呈现。 预期只支持横向布局。
userPortrait 在自动屏幕旋转和旋转锁定下,Activity 可以纵向或颠倒纵向呈现。 预期只支持纵向布局。
fullUser 在自动屏幕旋转和旋转锁定下,Activity 可以纵向或横向(以及颠倒纵向或横向)呈现。 预期同时支持纵向和横向布局。

旋转锁定用户将可选择锁定到颠倒纵向,通常为 180º。

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

例如,如果您的应用有一个用来切换前置和后置摄像头的按钮,则设备可能有多个前置或后置摄像头可供选择。 您应浏览一下摄像头列表,检查每个摄像头的特征,然后决定向用户显示哪些摄像头。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注