背景
GPU 驱动自升级,是在以下两个痛点下催生出来的:
1.换机频率降低,手机的生命周期越来越长 智能手机已经不再是一个增量市场,变成一个存量市场。消费者换机频率逐年降低,手机生命周期变长。生命周期变长了以后,那么如何更好地维护老机型就变成一个难题2.开发者需要升级 ROM 才能用到 GPU driver 的新功能 一直以来,游戏开发者想要升级到最新的 GPU 驱动的渠道跟普通的用户一样,只能靠 OEM 厂商的推送系统更新,他们没有一个渠道能够直接拿到最新的 GPU 驱动,因此无法第一时间对驱动的新特性进行适配。如何建立一个渠道能够让游戏开发者能够及时获取到最新的 GPU 驱动也是一个难题
有痛点就会有需求,GPU 驱动自升级就是针对这两个痛点提出的解决方案。
不过,这个方案要落地还会遇到很多实际问题:首先,GPU 驱动跟整个操作系统依赖极大,影响的模块很多,如 Display,Camera,Media 等,稍有不慎整个系统就会不稳定甚至 crash。
同时,这个功能要真正落地,还涉及到多方:GPU 驱动来自 SoC 厂商;Android 的 OEM 厂商等。每一方里涉及到厂商的又十分众多。因此在确定方案的时候需要保证能够有足够的兼容性。
以上这么多难题注定了这个方案从提出到落地,需要花很长的时间,走很长的路。
而事实也是如此,从 2017 年开始,Android O 提出了 Project Treble,引入了 VNDK 和 linker namespace,那时候其实已经在对这个方案进行技术铺垫了;再到 2018 年的 Android P,谷歌在 framework 加了很多 dummy code,此时整个方案的框架已经基本完成;再到 2019 年的 Android Q,这个方案作为 Q 的核心功能跟大家见面了。而小米十周年旗舰——小米 10/小米 10 Pro 也成为国内第一款落地 GPU 驱动自升级功能的 Android 智能手机。
在 GPU 驱动自升级方案中,可升级的 GPU 驱动分为两种,一种称为 Game Driver,面向的普通用户,主要是针对游戏这种十分依赖 GPU 的场景进行特殊优化,稳定的 driver;另一种称为 Prerelease Driver,面向游戏开发者,目的是让游戏开发者能够提前获取到包含新特性的,不稳定的驱动,进行提前的适配和利用。这两种驱动分别封装在两个 apk,分别通过两个 property 来分区:ro.gfx.driver.0
指定的是 Game Driver 的包名,ro.gfx.driver.1
指定的是 Prerelease Driver 的包名。
需要说明的是,虽然称为 apk,但是实际上更多地可以理解为是两个盛放 driver 的容器,apk 本身不具备任何的代码,而之所以要将其打包为 apk,更多的是为了利用 Android 的签名机制以及利用 PMS 能够对所有安装的 apk 进行统一的管理。
将 apk 解包以后可以放在整个包的结构如下:
├── AndroidManifest.xml├── assets│ ├── sphal_libraries.txt│ └── whitelist.txt├── lib│ ├── arm64-v8a│ └── armeabi-v7a└── res
分别说明一下各个结构的作用:
•sphal_libraries.txt 能够被 gfx driver
访问的, sphal
中的库列表,后面会详细讲解•whitelist.txt 使用 Game Driver 的包名白名单•lib driver 本体,分为 32 位和 64 位,根据应用的 abi 选择加载对应的 driver
Game Driver 和 Prerelease Driver (后面统称为 updated driver)这两个 apk 的结构是一样的,只不过 lib 文件夹里面的驱动不一样而已。
updated driver 配置流程
这部分讲讲在 app 启动的时候,是怎么给 app 挑选对应的 GPU driver 的。
app 在启动的过程中,经过下面的流转(详细的流程省略,非本文主题):
ActivityThread::handleBindApplication() \__ActivityThread::setupGraphicsSupport() \__GraphicsEnvironment.setup()
最终走到核心代码:
public void setup(Context context, Bundle coreSettings) {
final PackageManager pm = context.getPackageManager(); final String packageName = context.getPackageName(); .... if (!chooseDriver(context, coreSettings, pm, packageName)) {
setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName, getVulkanVersion(pm)); }}
核心函数 chooseDriver()
:
private static boolean chooseDriver( Context context, Bundle coreSettings, PackageManager pm, String packageName) {
// PART 1 final String driverPackageName = chooseDriverInternal(context, coreSettings); if (driverPackageName == null) {
return false; } ......}
chooseDriverInternal()
chooseDriverInternal()
,这个函数的主要作用是,根据系统的配置,为当前启动的 app 判断是否需要加载 Game/Prerelease Driver,如果需要那么返回对应的 driver apk 的包名;如果不需要,那么返回 null
,表明当前启动的 app 只需要加载 system gpu driver。判断的逻辑:
private static String chooseDriverInternal(Context context, Bundle coreSettings) {
// 判断 game driver 的 property 是否配置了 final String gameDriver = SystemProperties.get(PROPERTY_GFX_DRIVER); final boolean hasGameDriver = gameDriver != null && !gameDriver.isEmpty(); // 判断 prerelease driver 的 property 是否配置了 final String prereleaseDriver = SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE); final boolean hasPrereleaseDriver = prereleaseDriver != null && !prereleaseDriver.isEmpty(); // 两个 property 都没有配置,前面提到这两个 property 代表的是两个包名 // 如果都没有配置,那么说明这项功能没有使能,使用 system gpu driver if (!hasGameDriver && !hasPrereleaseDriver) {
if (DEBUG) Log.v(TAG, "Neither Game Driver nor prerelease driver is supported."); return null; } // To minimize risk of driver updates crippling the device beyond user repair, never use an // updated driver for privileged or non-updated system apps. Presumably pre-installed apps // were tested thoroughly with the pre-installed driver. // 不是所有的 app 都可以使用这个功能的,如果当前是预装应用,那么也不会对其使用该功能 final ApplicationInfo ai = context.getApplicationInfo(); if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
if (DEBUG) Log.v(TAG, "Ignoring driver package for privileged/non-updated system app."); return null; } // 接下来看下面的流程图:

在判断完当前的 app 需要使用的 driver 类型以后,返回到 chooseDriver()
。
chooseDriver()
private static boolean chooseDriver( Context context, Bundle coreSettings, PackageManager pm, String packageName) {
final String driverPackageName = chooseDriverInternal(context, coreSettings); if (driverPackageName == null) {
return false; } // PART 2 final PackageInfo driverPackageInfo; // 容纳 game driver 的 apk 必须是一个 system app,否则会直接跳过 try {
driverPackageInfo = pm.getPackageInfo(driverPackageName, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "driver package '" + driverPackageName + "' not installed"); return false; } // Android O 及以后,谷歌提出了 Project Triple,其中包括 VNDK,规定了 GPU driver 必须是在 sphal 的 linker space // 因此 driver apk 的 targetSdkVersion 必须大于等于 O // O drivers are restricted to the sphal linker namespace, so don't try to use // packages unless they declare they're compatible with that restriction. final ApplicationInfo driverAppInfo = driverPackageInfo.applicationInf