预置应用与 GMS:预置 APK 方法、GMS 包集成、权限预授权
做嵌入式 Android 系统,有一件事你肯定绕不开——预置应用。说白了,就是设备出厂时,桌面上那些已经装好的 App。不管是系统自带的计算器、相机,还是客户定制的行业应用,都得通过预置的方式塞进系统里。
我刚开始接触 AOSP 时,以为预置 APK 就是把 apk 文件扔到 system/app 目录下就完事了。结果呢?编译出来的镜像要么启动崩溃,要么应用权限不全。后来踩了不少坑,才摸清楚这里面的门道。
今天咱们就聊聊三件事:预置 APK 的几种方法、GMS 包的集成流程、以及权限预授权。这三块搞定了,定制 ROM 的基础能力就算拿下了。
预置 APK 的三种主流方式
预置 APK 不是简单地把文件拷贝进去。Android 系统对预置应用有严格的签名、权限、库依赖要求。我总结下来,常用的方法有三种。
方法一:直接预置到 system/app
这是最传统的方式。把 APK 放到 packages/apps/ 下,然后写一个 Android.mk 或 Android.bp 文件。
举个例子,假设你要预置一个叫 MyApp 的应用:
# Android.bp
android_app {
name: "MyApp",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
platform_apis: true,
certificate: "platform", // 使用平台签名
privileged: true, // 作为特权应用
}
这里有个关键点——certificate: "platform"。用平台签名意味着你的应用拥有系统级权限。我在项目中遇到过,如果不用平台签名,预置的应用可能无法访问某些系统 API,比如读写短信记录。
方法二:预置到 product 分区
从 Android 10 开始,Google 推荐把厂商定制应用放到 product 分区。这样做的好处是,系统 OTA 升级时可以只更新 system 分区,不影响 product 分区里的应用。
目录结构一般是:
device/<vendor>/<product>/apps/MyApp/
├── Android.bp
├── MyApp.apk
└── AndroidManifest.xml
对应的 Android.bp 写法:
android_app_import {
name: "MyApp",
apk: "MyApp.apk",
privileged: true,
presigned: true, // 使用 APK 自带的签名
product_specific: true,
}
我个人习惯用 android_app_import 而不是 android_app,因为很多时候客户给的是编译好的 APK,我们不需要重新编译源码。直接导入就行,省事。
方法三:预置到 data/app(可卸载)
有些应用需要预置,但允许用户卸载。这时候可以放到 data/app 目录。不过要注意,这种方式需要额外的配置。
在 device.mk 中添加:
PRODUCT_PACKAGES += MyApp
# 标记为可卸载
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
data/app/MyApp/MyApp.apk
嗯,这里有个坑——如果应用预置在 data 分区,恢复出厂设置时会被清除。所以银行类、支付类的核心应用,千万别用这种方式。
GMS 包集成:Google 服务的正确姿势
GMS(Google Mobile Services)是 Google 官方服务的集合,包括 Play Store、Gmail、地图等。如果你做的是面向海外市场的设备,GMS 几乎是必选项。
但 GMS 不是开源的,也不能随便集成。你需要和 Google 签订 GMS 授权协议,拿到 GMS 包。这个包通常是一个 ZIP 文件,里面包含几十个 APK 和对应的库文件。
集成步骤
拿到 GMS 包后,集成流程大致如下:
- 解压 GMS 包,你会看到
apps/、etc/、framework/等目录。 - 将 APK 放到对应位置。大部分 GMS 应用需要预置到
system/priv-app目录,因为它们是特权应用。 - 添加权限配置。GMS 应用需要大量权限,比如位置、联系人、电话等。这些权限需要在
system/etc/permissions/下声明。 - 配置产品 Makefile。在
device.mk中引用 GMS 包:
# 引用 GMS 包
$(call inherit-product, vendor/google/products/gms.mk)
# 或者手动添加
PRODUCT_PACKAGES += \
GooglePlayServices \
GooglePlayStore \
GoogleServicesFramework \
...
vendor/google/ 目录下,不要混入 AOSP 源码。这样方便后续升级 GMS 版本,也符合 Google 的合规要求。
GMS 认证注意事项
集成 GMS 后,设备需要通过 CTS(Compatibility Test Suite)和 GTS(Google Test Suite)认证。我遇到过最头疼的问题是——某些 GMS 应用在预置后无法正常启动,原因是缺少 Google 服务框架的签名。
解决方案是:确保所有 GMS 应用都使用 Google 的 platform 签名,或者使用 GMS 包自带的签名文件。千万不要自己重新签名,否则 Google 服务会拒绝工作。
权限预授权:让应用开箱即用
你有没有遇到过这种情况?设备刚开机,用户打开预置的相机应用,结果弹出一堆权限请求框。用户体验很差,对吧?
权限预授权,就是提前把某些权限授予预置应用,让用户不用手动确认。说白了,就是系统替用户做了决定。
实现方式
在 AOSP 中,权限预授权主要通过 AndroidManifest.xml 中的 <uses-permission> 标签,以及系统级的 permissions.xml 文件来实现。
具体做法是:
- 在应用的 AndroidManifest.xml 中声明权限。这是基础,不声明的话系统根本不会授予。
- 在系统配置中预授权。修改
frameworks/base/core/res/res/xml/config_perm.xml,或者使用overlay机制覆盖。
举个例子,假设你要预授权 MyApp 使用相机和位置权限:
<!-- config_perm.xml -->
<permissions>
<privapp-permissions package="com.example.myapp">
<permission name="android.permission.CAMERA"/>
<permission name="android.permission.ACCESS_FINE_LOCATION"/>
</privapp-permissions>
</permissions>
然后把这个文件放到 system/etc/permissions/ 目录下。编译时系统会自动读取并授予权限。
grantRuntimePermission() 方法。所以只要系统配置到位,应用一安装就拥有了指定权限。
避坑指南
我曾经在项目中犯过一个错误——把预授权配置写到了 vendor 分区,但应用预置在 system 分区。结果权限配置没生效,因为系统在解析权限时,是按分区顺序加载的。
正确的做法是:预授权配置文件和预置应用必须在同一个分区。如果应用在 system/priv-app,配置也要放在 system/etc/permissions。
另外,Android 11 之后,Google 对权限预授权收紧了。非特权应用(非 priv-app)无法通过 privapp-permissions 标签预授权。所以如果你的应用不是特权应用,预授权会失败。解决办法就是让应用成为特权应用,或者使用 grant-uri-permission 等更细粒度的控制方式。
知识体系总览
下面这张图总结了预置应用与 GMS 集成的核心流程和决策点:
写在最后
预置应用这件事,说简单也简单,说复杂也复杂。简单在于,你只要把 APK 放对位置、写对配置文件,编译出来就能用。复杂在于,签名、分区、权限、GMS 合规,任何一个环节出问题,设备就可能无法正常工作。
我个人建议,刚开始做定制 ROM 时,先从 product 分区预置入手。这种方式最灵活,也最容易调试。等把流程跑通了,再根据需求调整到 system 或 data 分区。
至于 GMS,如果你不是做海外市场,可以跳过。但如果要做,一定要走正规授权渠道,别用网上流传的破解包。我见过有人用盗版 GMS 包,结果设备被 Google 远程封禁,用户投诉到崩溃。
权限预授权这块,记住一个原则:只预授权应用真正需要的权限。别图省事把所有权限都给了,那样反而会带来安全风险。
公众号:蓝海资料掘金营,微信deep3321