用户可以在设备上启用手势导航,启用后设备上的所有app都会受到影响,如从屏幕边缘划入,若app没有专门覆盖该手势,系统会将该手势翻译为后退。需要开发者做好手势冲突兼容。
为了帮助确保应用稳定性和兼容性,Android 平台开始限制您的应用可在 Android 9(API 级别 28)中使用哪些非 SDK 接口。Android Q 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。我们的目标是在限制使用非 SDK 接口之前确保有可用的公开替代方案。
如果您不打算以 Android Q 为目标平台,那么其中一些变更可能不会立即对您产生影响。虽然您目前可以使用灰名单中的一些非 SDK 接口(取决于您应用的目标 API 级别),但如果您使用任何非 SDK 方法或字段,则应用无法运行的风险终归较高。
如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试该应用进行确认。如果您的应用依赖于非 SDK 接口,则应该开始计划迁移到 SDK 替代方案。不过,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,则应该请求新的公共 API。
针对非 SDK 接口的限制
非 SDK 接口在 Android Q 中的受限情况出现变化
Android Q 包含以下 NDK 方面的变更。
共享对象不得包含文本重定位
Android 6.0(API 级别 23)已禁止在共享对象中使用文本重定位。代码必须按原样加载,且不得修改。此变更可以缩短应用的加载时间并提高安全性。
在 Android Q 测试版 1 和 2 中,SELinux 对以 Android 8.0(API 级别 26)及更高版本为目标平台的应用强制执行此限制。从 Android Q 测试版 3 开始,将对以 Android Q(API 级别 29)及更高版本为目标平台的应用强制执行此限制。如果这些应用继续使用包含文本重定位的共享对象,则出现故障的风险较高。
从 Android Q 开始,多个路径不再采用常规文件形式,而是采用符号链接形式。如果应用一直以来依赖的都是采用常规文件形式的路径,则可能会出现故障:
这些变更也会影响文件的 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 Q 开始,系统二进制文件和库会映射到只执行(不可读取)内存,作为应对代码重用攻击的安全强化技术。有意或意外读入已标记为只执行的内存段会抛出 SIGSEGV,无论此读入行为是来自错误、漏洞还是有意的内存自省都不例外。
您可以通过检查 /data/tombstones/
中的相关 tombstone 文件来确定崩溃是否由变更改所导致。与只执行相关的崩溃包含以下中止消息:
Cause: execute-only (no-read) memory access error; likely due to data in .text.
要解决此问题,开发者可以通过调用 mprotect()
将只执行内存段标记为“读取+执行”,例如用于执行内存检查。不过,我们强烈建议您事后将其重新设为只执行,因为这样可以更好地保护您的应用和用户。
对 ptrace
的调用不会受到影响,因此 ptrace
调试也不会受到影响。
Android Q 包含以下安全性方面的变更。
移除了应用主目录的执行权限
以 Android Q 为目标平台的不受信任的应用无法再针对应用主目录中的文件调用 exec()
。这种从可写应用的主目录执行文件的行为违反了 W^X。应用应该仅加载嵌入到应用的 APK 文件中的二进制代码。
此外,以 Android Q 为目标平台的应用无法针对已执行 dlopen()
的文件中的可执行代码进行内存中修改。这包括含有文本重定位的所有共享对象 (.so
) 文件。
在 Android Q 中,以下与 WLAN 直连相关的广播不再具有粘性。
WIFI_P2P_CONNECTION_CHANGED_ACTION
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
如果您的应用依赖于在注册时接收这些广播(因为其之前一直具有粘性),请在初始化时使用适当的 get()
方法获取信息。
Android Q 扩大了支持范围,现在可以使用 WLAN 感知数据路径轻松创建 TCP/UDP 套接字。要创建连接到 ServerSocket
的 TCP/UDP
套接字,客户端设备需要知道服务器的 IPv6 地址和端口。这在之前需要通过频外方式进行通信(例如使用 BT 或 WLAN 感知第 2 层消息传递),或者使用其他协议(例如 mDNS)通过频内方式发现。而借助 Android Q,可以将此类消息作为网络设置的一部分进行传递。
服务器可以执行以下任一操作:
ServerSocket
并设置或获取要使用的端口。以下代码示例显示了如何将端口信息指定为网络请求的一部分:
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();
然后,客户端会执行 WLAN 感知网络请求来获取服务器提供的 IPv6 和端口:
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);
在 Android Q(Go 版本)设备上运行的应用无法获得 SYSTEM_ALERT_WINDOW
权限。这是因为绘制叠加层窗口会使用过多的内存,这对低内存 Android 设备的性能十分有害。
如果在搭载 Android 9 或更低版本的 Go 版设备上运行的应用获得了 SYSTEM_ALERT_WINDOW
权限,则即使设备升级到 Android Q 也会保留此权限。不过,尚不具有此权限的应用在设备升级后便无法获得此权限了。
如果 Go 设备上的应用发送具有 ACTION_MANAGE_OVERLAY_PERMISSION
操作的 intent,则系统会自动拒绝此请求,并将用户转到设置屏幕,上面会显示不允许授予此权限,原因是它会减慢设备的运行速度。如果 Go 设备上的应用调用 Settings.canDrawOverlays()
,则此方法始终返回 false。同样,这些限制不适用于在设备升级到 Android Q 之前便已收到 SYSTEM_ALERT_WINDOW
权限的应用。
在 Android Q 中,当用户首次运行以 Android 6.0(API 级别 23)以下的版本为目标平台的任何应用时,Android 平台会向用户发出警告。如果此应用要求用户授予权限,则系统会先向用户提供调整应用权限的机会,然后才会允许此应用首次运行。
由于 Google Play 的目标 API 方面的要求,用户只有在运行最近未更新的应用时才会看到这些警告。对于通过其他商店分发的应用,我们也将于 2019 年引入类似的目标 API 方面的要求。要详细了解这些要求,请参阅在 2019 年扩展目标 API 级别方面的要求。
以下 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 变体,要么二者均不支持。
注意:应用和库应该让其所需的加密套件集与 getSupportedCipherSuites()
中返回的值相交,以便提前防范加密套件日后遭到移除。
Android Q 引入了与应用使用情况相关的以下行为变更:
UsageStats 应用使用情况的改进 – 当在分屏或画中画模式下使用应用时,Android Q 现在能够使用 UsageStats 准确地跟踪应用使用情况。此外,Android Q 现在可以跟踪免安装应用的使用情况。
按应用开启灰度模式 – Android Q 现在可以将应用设为灰度显示模式。
按应用开启干扰模式 – Android Q 现在可以选择性地将应用设为“干扰模式”,此时系统会禁止显示其通知,并且不会将其显示为推荐的应用。
暂停和播放 – 在 Android Q 中,暂停的应用无法再播放音频。
如果运行 Android Q 的应用将 null
传递给 setSSLSocketFactory()
,现在会出现 IllegalArgumentException
。在以前的版本中,将 null
传递给 setSSLSocketFactory()
与传入当前的默认 SSL 套接字工厂效果相同。
android.preference
库现已弃用。开发者应该改为使用 AndroidX preference
库,这是Android Jetpack 的一部分。
Android Q 对 java.util.zip
软件包(用于处理 ZIP 文件)中的类做出了以下变更。这些变更会让库的行为在 Android 和使用 java.util.zip
的其他平台之间更加一致。
Inflater
在以前的版本中,如果在调用 end()
之后调用 Inflater
类中的某些方法,这些方法会抛出 IllegalStateException
。在 Android Q 中,这些方法会抛出 NullPointerException
。
ZipFile
如果所提供的 ZIP 文件不包含任何文件,则 ZipFile 的构造函数(采用的参数类型为 File、int 和 Charset)不再抛出 ZipException
。
ZipOutputStream
如果 ZipOutputStream 中的 finish()
方法尝试为不包含任何文件的 ZIP 文件写入输出流,此方法不再抛出 ZipException
。
很多使用摄像头的应用都会假定如果设备采用纵向配置,则物理设备也会处于纵向。在过去可以做出这样的假定,但在推出新型设备(例如可折叠设备)后,这发生了变化。针对这些设备做出这样的假定可能导致相机取景器的显示产生错误的旋转和/或缩放。
以 API 级别 24 或更高级别为目标平台的应用应该明确设置 android:resizeableActivity
,并提供必要的功能来处理多窗口操作。
从 Android Q 开始,只有在发生重大充电事件之后拔下设备电源插头, SystemHealthManager
才会重置其电池用量统计信息。一般来说,重大充电事件指的是设备电池已充满,或者设备电量从几乎耗尽变为即将充满。
在 Android Q 之前,无论何时拔下设备电源插头,无论电池电量有多微小的变化,电池用量统计信息都会重置
Ashmem 更改了 /proc/
中的 dalvik 映射的格式,这会影响那些直接解析映射文件的应用。如果应用依赖于 dalvik 映射格式,则应用开发者应该在设备上测试新的 /proc/
格式并相应地进行解析。
以 Android Q 为目标平台的应用无法再直接使用 ashmem (/dev/ashmem)
,而必须通过 NDK 的 ASharedMemory
类访问共享内存。此外,应用无法直接对现有 ashmem 文件描述符进行 IOCTL,而必须改为使用 NDK 的 ASharedMemory
类或 Android Java API 创建共享内存区域。这项变更可以提高使用共享内存时的安全性和稳健性,从而提高 Android 的整体性能和安全性。
如果应用以 Android Q 或更高版本为目标平台并使用涉及全屏 intent 的通知,则必须在清单文件中请求 USE_FULL_SCREEN_INTENT
权限。这是普通权限,因此,系统会自动为请求权限的应用授予此权限。
如果以 Android Q 或更高版本为目标平台的应用试图创建使用全屏 intent 的通知,而不请求 USE_FULL_SCREEN_INTENT
权限,则系统会忽略此全屏 intent 并输出以下日志消息:Package [pkg]: Use of fullScreenIntent requires the USE_FULL_SCREEN_INTENT permission
Android 运行时 (ART) 不再从应用进程调用 dex2oat
。这项变更意味着 ART 将仅接受系统生成的 OAT 文件。
过去,如果编译时和运行时的类路径环境不同,则 Android 运行时 (ART) 执行的预先 (AOT) 编译可能会导致运行时崩溃。Android Q 现在始终要求这些环境上下文相同,因而导致出现了以下行为变更:
Android Q 包含支持可折叠设备和大屏设备的变更。
当应用在 Android Q 上运行时,onResume()
和 onPause()
方法的工作原理是不同的。当多个应用同时在多窗口模式或多显示屏模式下显示时,可见堆栈中所有可设置为焦点的顶层 Activity 都处于“已恢复”状态,但实际上焦点仅位于其中一个 Activity 上,即“在最顶层处于已恢复状态”的 Activity。在 Android Q 之前的版本中运行时,一次只能恢复系统中的一个 Activity,而所有其他可见 Activity 都处于已暂停状态。
请不要将“焦点位于”的 Activity 与“在最顶层处于已恢复状态”的 Activity 混淆。系统会根据 Z-Order 来为 Activity 分配优先级,以便为用户最后进行互动的 Activity 提供更高的优先级。Activity 可能在顶层处于已恢复状态,但焦点却并不位于其上(例如,如果通知栏展开)。
在 Android Q 中,您可以订阅 onTopResumedActivityChanged()
回调,以便在 Activity 获取或失去在最顶层处于已恢复状态的位置后收到通知。这相当于 Android Q 之前版本中的已恢复状态;如果您的应用使用的专用或单一资源可能需要与其他应用共享,这可以作为有用的提示。
resizeableActivity
清单属性的行为也发生了变化。如果某个应用在 Android Q 中设置 resizeableActivity=false
,则当可用屏幕尺寸发生变化或者该应用从一个屏幕移到另一屏幕时,它可能处于兼容模式下。
应用可以使用新的 android:minAspectRatio
属性来指示应用是否支持新的屏幕宽高比。
从版本 3.5 开始,Android Studio 的模拟器工具将包含 7.3" 和 8" 的虚拟设备,以便您使用 Android Q 系统映像测试代码。
如需了解详情,请参阅打造适用于可折叠设备的应用。
非标准文件(例如 /dev/zero
)已不再支持 FileChannel.map()
,其大小无法使用 truncate()
进行更改。之前的 Android 版本会生吞掉 truncate()
返回的错误,但 Android Q 会抛出 IOException
。如果您需要旧行为,则必须使用原生代码。