如何在 Android 设备上批量预装应用
应用究竟如何进入整支设备群并保持最新:进入设备的交付路径、预装应用与系统应用的区别,以及批次发货之后由谁负责签名密钥、版本和回滚。
- 发布于
- 更新于

简要答案
把一个应用装进整支设备群,是两个决策而不是一个;把它当成一个决策的项目,通常要在之后为第二个决策付出代价。第一个决策是交付路径:安装包究竟如何进入设备——由管理平台在注册之后安装、在预配台的预配环节写入、写进出厂镜像使其在首次开机时即存在,还是以更高的权限地位集成进系统分区。第二个决策是更新责任归属:谁持有签名密钥、谁决定发布新版本、版本如何在配置记录中被锁定,以及要撤回一个有问题的版本,在实际操作和商务上分别需要发生什么。两者互相制约。应用在配置版本中埋得越深,它能做的事越多,改动的代价也越高,因为改动不再是一次应用商店发布,而成为需要 OEM 参与的固件事件。这一取舍正是本页的全部主题。有一条边界值得在开头就说明:本指南讨论的是应用进入设备的路径,而不是由哪个分发渠道承载安装包——公开 Google Play、受管 Google Play 还是自行托管的 APK,是一项相关但自有约束的决策,需要与下文的放置层级决策分开确定。
应用如何进入设备:四类路径
这些路径写在需求文档里看似可以互换,落到现场表现却截然不同。受管安装发生在设备注册之后:在预配时被设为设备所有者的设备策略控制器(DPC)按管理平台的指令安装应用,之后应用的更新、替换或移除也走同一条路径。预配环节装机则在预配作业过程中、设备装箱之前就把安装包装上,因此无论设备之后是否保持常态化管理关系,出厂时应用都已存在。工厂预装把安装包写进出厂镜像;当应用必须在一台可能永远不会注册、也可能永远连不上网络的设备上于首次开机时就存在,这就是应当选择的路径。系统集成再深一层,把应用放到系统分区上——这与预装是两回事,下文单列一节讨论。受管路径变更成本最低、也最容易验证,因此有用的纪律是:任何更重的做法都必须给出理由,而不是默认采用;标准的受管分发行为——包括会阻断面向用户的卸载入口的安装类型——记录在 Android Management API 应用分发指南中。下文每一条路径都取决于 OEM 和平台,需要逐型号确认,而不能从设备系列名称推断。
| 交付路径 | 由谁控制 | 如何更新 | 用户能否卸载? | 恢复出厂设置后能否保留? | 样机必须证明什么 |
|---|---|---|---|---|---|
| 受管安装,注册后强制安装 | 管理平台,以及被设为设备所有者的 DPC | 向受管渠道发布新版本,设备在下次签入时应用 | 不能——只要策略生效,该安装类型就会阻断面向用户的卸载入口 | 不直接保留;设备经同一条预配路径重新注册后,应用会被重新安装 | 从干净设备出发,应用在约定时间窗内无人值守地到达,且启动器、设置和应用信息中确实没有卸载入口 |
| 受管安装,作为可选应用提供 | 管理平台,由用户自行决定何时安装 | 同一受管渠道,但只作用于用户已安装该应用的设备 | 可以——用户可以卸载,也可能从未安装 | 不能——重新安装取决于用户再次操作 | 工作流程能够容忍应用缺失的设备,以及如何检出这种状态 |
| 预配台上的预配环节装机 | 预配流程;移交时责任转交客户 | 没有自动机制——后续版本需要管理渠道、应用商店,或把设备送回预配台 | 可以,除非设备所有者策略阻止卸载 | 不能——重置会将其清除,除非预配路径会重新应用整套配置 | 已预配设备的应用包名、版本号和签名者与参考样机一致,并逐台核验 |
| 工厂预装:从 OEM 预装区写入数据区 | OEM 配置版本,在设备配置规范中约定 | 若存在应用商店或受管渠道则走该渠道;否则是一次固件事件 | 通常可以——首次开机复制到位后,它的行为与普通已安装应用一致 | 通常可以——重置会从预装区将其恢复,但这取决于 OEM | 在无网络条件下首次开机即存在,且在有记录的恢复出厂设置之后会重新出现 |
| 只读分区上的预装系统应用 | 仅由 OEM 配置版本决定——应用团队提供的是安装包,而不是放置方式 | 更新会覆盖安装到数据区;出厂版本仍留在下层 | 不能——用户可以卸载更新回到出厂版本,也可能可以停用它 | 可以——重置不会触及该分区,因此出厂版本会回来 | 出厂版本就是已批准的版本、更新能干净地覆盖安装,且回退之后落到一个可用版本上 |
| 特权或平台签名的系统应用 | OEM 配置版本,以及 OEM 的签名流程 | 固件层变更,或仅在特定条件下才保留高权限放置的数据区更新 | 不能——不提供卸载入口;能否停用取决于配置版本 | 可以——它是镜像的一部分 | 出厂配置版本上确实授予了这些高权限,且在应用存在的情况下该版本能正常启动并通过兼容性测试 |
应用预装与系统应用集成的区别
这是最常被含糊带过的一个区分,因为在会议上两者都被说成“应用已经预装了”。它们其实是两种不同的工程承诺,责任方也不同。预装是把一个普通应用放置到位,使用户第一次开机时它就在那里。系统应用集成改变的则是这个应用本身是什么:它所在的分区、它的权限地位、它的卸载行为,以及它与今后每一个固件构建的关系。有五种放置方式值得分开来看,且逐级升高。普通已安装应用位于数据区,由应用团队签名,除非策略阻止否则可以卸载,恢复出厂设置后消失——从头到尾由应用团队负责。在首次开机时从 OEM 预装区或定制区复制到数据区的预装应用,在用户看来完全相同,但重置后会被恢复;安装包仍由应用团队负责,放置方式则由 OEM 负责。预装系统应用位于 system 或 product 镜像这类只读分区上:用户看不到卸载选项,只能卸载后续更新,而由于该分区从不被擦除,重置之后出厂版本会回来。特权应用再进一步——它被放在特权应用目录中,可以持有框架不会授予普通应用的权限,而这些权限必须出现在该配置版本的特权权限白名单里,该机制记录在 AOSP 特权权限白名单参考文档中。当强制校验开启时,若某个特权应用申请的权限不在该白名单中,该配置版本可能直接拒绝启动——这是一次在工作台上就会暴露的固件级故障,而不是来自现场的缺陷报告。平台签名应用是最后一级:它使用 OEM 的平台密钥签名,该密钥由 OEM 掌握且极少对外提供,并把应用绑定到签名级权限和该厂商的配置版本上。在做出承诺之前,有三条后果值得直说。第一,普通安装以上的所有做法都需要 OEM 的配置版本工程参与——应用团队无法再独立发布制品,这种放置方式的可得性完全取决于 OEM 的意愿和排期。第二,每一级都会在每次固件变更时重新验证:一个安全补丁、一次 Android 版本升级或一个区域版本,都会重新打开“放置方式、权限和白名单是否仍然成立”这个问题,因此系统集成应当被记录为一项长期维护义务,而不是一件已完成的任务。第三,每一级都会关掉一些选项。平台签名把应用绑定在单一厂商上,日后切换第二货源硬件会变得复杂。特权放置会把候选设备名单收窄到愿意做这项集成的厂商,并可能影响该配置版本的兼容性提交。而系统级放置会改变回滚的说法:普通应用回滚只需发布一个修正版本,镜像里放错的系统级放置则要靠一次固件发布、把设备送回预配台,最坏的情况是这批设备带着一条已知限制出货。诚实的规划原则是:把应用保持在能满足需求的最轻一级,只有当书面需求确实无法由下一级承载时才升级,并且把这次升级当作设备选型决策而不是软件决策——这一论点的完整版见MDM 与定制 Android ROM 对比。
| 放置层级 | 由谁承担工作 | 所需签名 | 用户卸载或停用 | 系统或固件更新对它的影响 | 它关掉了什么,以及如何回滚 |
|---|---|---|---|---|---|
| 普通已安装应用(数据区) | 仅应用团队;无需 OEM 参与 | 应用团队自有的发布密钥,各版本保持一致 | 可卸载,除非设备所有者策略阻止 | 作为普通应用在更新后保留;行为变化来自新的 API 级别,而不是放置方式 | 不关掉任何选项。回滚就是通过同一渠道发布一个修正版本 |
| 首次开机时写入数据区的预装应用 | 应用团队提供安装包;OEM 将其放入配置版本 | 应用团队的发布密钥——但必须与日后负责更新的渠道所用密钥一致 | 首次开机复制到位后通常可卸载;重置会将其恢复 | 一般会保留,但预装区因 OEM 而异,每个配置版本都需重新确认 | 镜像冻结后就无法再临时更换版本。回滚意味着做新镜像,或通过渠道更新覆盖安装 |
| 预装系统应用(只读分区) | OEM 配置版本工程,依据约定的设备配置规范 | OEM 配置版本接受应用团队的密钥;此后该密钥在该镜像的整个生命周期内固定 | 不提供卸载;用户可以卸载后续更新,也可能可以停用它 | 出厂副本会被新镜像替换;数据区中的更新是否保留,取决于版本号和更新流程 | 无法再独立发布低于当前镜像版本的热修复。回到可用状态意味着卸载更新,或发布一次固件 |
| 特权系统应用(特权目录加权限白名单) | OEM 配置版本工程,每项特权权限都要有一条权限白名单条目 | 应用团队密钥固定在镜像中,加上与该签名者绑定的白名单条目 | 不提供卸载;能否停用取决于配置版本,需在样机上确认 | 每个配置版本都要重新验证——强制校验开启时,白名单遗漏可能导致该版本无法启动 | 无法再维持宽泛的候选设备名单,并会影响该配置版本的兼容性提交。回滚是一次固件事件,需等待 OEM 的交付周期 |
| 平台签名应用 | OEM 配置版本工程,加上 OEM 的平台签名流程 | OEM 平台密钥,由 OEM 持有且极少对外提供 | 不提供卸载;该应用属于平台信任边界的一部分 | OEM 每出一个配置版本,都必须重新签名并重新集成 | 无法再采用第二货源硬件,也无法独立分发同一个已签名制品。回滚是一次固件事件,而任何密钥变更都等同于一个迁移项目 |
决定哪些路径仍然可用的安装包前提条件
在选定路径之前,必须先弄清安装包本身,因为有几项安装包属性会悄悄关掉某些路径。应用包名和签名身份必须在各版本间保持稳定:正是它们把一次更新绑定到已有的安装上,而一旦不匹配,后果不会平滑降级——若更新所用密钥与设备上已有副本不同,安装会被直接拒绝,因此用开发密钥签名的预装应用,可能永远无法被量产更新渠道触及。运行时权限有两重意义:一重是应用运行本身所需,另一重是设备所有者策略可以静默授予或拒绝其中一部分,这会改变首次运行测试实际能证明的内容。对 Google Play 服务的依赖是一项路径决策而不是细节,因为假定其存在的安装包在不含它的配置版本上表现会不同;GMS 与 AOSP 企业设备对比说明了这一选择会失去什么。原生库必须与设备的 CPU 架构(ABI)匹配,只针对一种架构构建的安装包会悄悄排除掉候选设备名单中的一部分。目标 API 级别、后台执行方面的假设、前台服务类型,以及对成为某个 intent 默认处理程序的任何依赖,都需要对照目标配置版本上的确切 Android 版本核查,而不是对照应用开发时所用的版本。这些都不是什么冷僻问题;它们只是决定最轻的那条路径能否走通的属性,而且每一项在第一周就确认,都比在预配作业中才发现便宜得多。应用团队应逐项走查的完整预检清单,见应用就绪 Android 设备检查清单。
- 应用包名与签名身份在各版本间保持稳定,并与更新渠道一致
- 运行时权限按最小权限原则审查,并对照策略会静默授予的内容核对
- 在候选设备名单确定之前,先就 GMS 还是 AOSP 目标厘清对 Play 服务的依赖
- 原生库与范围内每一款机型的设备 CPU 架构匹配
- 目标 API 级别、后台执行和默认处理程序方面的假设,在确切配置版本上核查
交付之后由谁负责更新
交付是容易的那一半。决定整支设备群能否保持健康的另一半,是设备到达现场之后由谁负责版本,而它可以拆成四个具体问题——这些问题属于合同,而不属于口头沟通。第一个是密钥保管。谁持有签名密钥,谁才具备发布更新的能力,因为平台只接受与已安装副本签名身份相同的新版本。如果应用通过代发布者管理签名密钥的应用商店分发,那么交给工厂做预装的制品必须就是该商店将要分发的那一个,而不是同一份源码在本地签名的构建——否则在设备看来,预装副本和商店副本是两个不同的应用,商店更新无法覆盖安装到预装副本之上。其中的机制——包括上传密钥与签名密钥的区别,以及可以轮换密钥的有限情形——记录在 Android 应用签名指南中。密钥保管还附带一个退出问题:如果与集成商、OEM 或分销商的合作关系终止,届时还有谁能为发行版本签名。第二个问题是版本锁定。只有把已批准的版本以确切的版本号和构建标识写进Android 设备配置规范,并连同它获得验收时所对应的固件构建一并记录,部署才是可复现的。一份只写了应用名称却没有锁定版本的设备配置规范,等于什么都没锁定,因为制品是会变的。锁定版本还给批次提供了一项可执行而非靠假设的核验:每台已预配设备都会报告一个版本,该版本要么与记录一致,要么这台设备就此停下。第三个问题是回滚,也是最常被想当然、而不是被设计出来的一项。在 Android 上,已安装的应用通常不接受版本号更低的安装包,而平台层面确实存在的回滚支持范围狭窄,且取决于安装程序。实际操作中,回滚意味着发布一个版本号更高、但内容是旧代码的版本——而这只有在旧代码仍可构建、仍可签名,并且仍与问题版本所产生的后端状态兼容时才可行。对预装应用来说情况更难,因为镜像里的那份副本不会变:卸载更新会把设备退回出厂版本,而该版本可能比设备群预期的更旧;要修正镜像本身,则是一次需要等待 OEM 交付周期的固件事件。一份经得起现实检验的回滚方案,会写明制品、能为其签名的人、承载它的渠道、预计送达设备的时间,以及在此期间设备群如何运作。第四个问题是那句应该拿去测试而不是直接相信的话:“应用会自己更新”。这句话可能指应用商店托管的更新、由管理平台驱动的更新、应用内自行下载安装包的更新程序,也可能在缺少更新程序所依赖组件的配置版本上什么都不是。自更新路径经常需要一项已被设备所有者策略限制的安装权限、一条被锁定配置所禁止的网络访问,或者一个目标配置版本中并不存在的商店组件。应把它当作一条必须附带测试的声明:在已验收样机、已验收配置版本上发布一个新版本,观察它是否到达、耗时多久、设备是否需要解锁或有人值守,以及当更新在班中、用户正在处理任务时落地会发生什么。更新责任归属也是一项移交事项,因为设备群的寿命长于项目本身:指明由谁发布、由谁审批,以及某次发布出问题时该找谁。
- 指明密钥保管方,包括商务关系终止后还有谁能为发行版本签名
- 在设备配置规范中,对应一个具名固件构建锁定确切的版本号和构建标识
- 回滚设计为把旧代码作为新版本向前发布,并指明签名方、渠道和预计送达时间
- 自更新行为在已验收配置版本上实测,而不是当作产品说明直接接受
- 在移交之前就指定发布人、审批人和升级联系人,而不是等第一次发布出问题之后
在受版本控制的样机上验证交付与更新
在有一台设备把它演示出来之前,交付路径只是一个假设。受版本控制的样机把确切型号与区域 SKU、Android 版本与固件构建、应用包名、版本号与签名者、预配路径以及策略版本全部固定下来,从而使本页的每一项陈述都挂在可复现的对象上,而不是挂在某个设备系列上。交付证据是行为性的,且从干净设备开始:把设备恢复出厂设置,走一遍既定预配路径,并记录在无人操作的情况下应用出现并可用所需的时间。然后测试真实会发生的状态——无网络开机的设备、预配数周之后才第一次开机的设备、被中断的首次开机设置、存储空间不足的设备,以及用户试图卸载该应用的设备。更新证据需要在同一台设备上再做一次动作:发布新版本、观察其到达、确认设备上报的版本与已发布版本一致,然后按回滚方案指定的路径退回上一版本,并确认设备仍然可用。卸载证据用来闭环:先阻止卸载,再分别从启动器、设置、应用信息,以及该配置允许的任何文件管理器或处理程序尝试卸载,并记录系统提供了哪些入口。结果应当落进结构化记录,而不是聊天记录,这正是样机验收矩阵的用途——场景、预期行为、实测行为、结论、责任人——而任何无法关闭的问题都写进已知限制库,让审批人在签字之前就看到。批次预配随后复现已验收状态,而不是重新发明它:相同的镜像、相同的安装包与版本号、相同的预配路径,逐台与参考样机比对,并约定设备出现偏差时的停止规则,具体见交付前的 Android 设备批次预配。这也是为什么项目报价从 500 台左右起,并在项目内部先安排一个二十到一百台设备的试点批次:交付与更新路径正是在试点中于真实条件下得到确认,其余设备只有在这些证据成立之后才进入预配。
- 一台受版本控制的样机固定型号、SKU、固件构建、安装包、版本号、签名者、预配路径和策略版本
- 交付时间从恢复出厂设置的设备开始、经真实预配路径、在无人值守条件下计时
- 更新和回滚都要在同一台设备上实测,而不只是验证首次安装
- 从该配置留下的每一个可达入口尝试卸载,并记录结果
- 批次预配复现已验收状态,并在设备偏离参考基准时停下
交付链条中代码与凭据的处理
准备一支设备群,意味着要经手别人的代码,往往还包括他们的凭据。相关控制措施平淡无奇,却仍然是最常被跳过的那些。安装包传输受控,制品在进入配置版本或预配流程之前先行核验:把收到的文件与应用团队公布的校验和比对,而不是比对文件名。测试账户使用为该项目签发、并在移交时吊销的最小权限凭据,绝不使用生产密钥,因为预配台是共享环境,而内嵌生产令牌的 APK 是一个比测试失败存在得更久的问题。应用在设备上能触及的范围,通过它申请的权限和外围所应用的策略有意识地加以界定;任何高权限放置都必须有书面理由,而不是因为方便就予以授予。这些是在项目实施期间落实并验证的作业规范,结果与其余验收证据一并记录——此处将其描述为控制措施,而不是对任何部署毫无风险的担保。
- 受控的安装包传输,并与应用团队公布的校验和比对
- 测试账户使用最小权限的项目凭据,并在移交时吊销
- 通过所申请的权限和所应用的策略界定应用的数据访问范围
- 高权限放置须有书面理由,而不是图方便就予以授予
预装路径的已知限制
上述框架是一件规划工具,值得说明它在哪里不再那么整齐。路径的可得性自始至终取决于 OEM、Android 版本、EMM 和硬件,因此在一款机型上确认过的路径,并不等于在其后继机型或同一机型的另一个区域版本上也已确认。
- 普通安装以上的任何做法都需要 OEM 的配置版本工程参与,其可得性完全取决于该 OEM 的排期与意愿。
- 系统级和特权级放置会在每次固件变更时重新验证,包括安全补丁和区域版本。
- 预装副本与更新渠道之间的签名密钥不匹配无法就地修复——只能替换该应用。
- 恢复出厂设置会擦除数据区:受管安装和预配台装机的应用无法保留,系统分区上的放置则可以。
- 应用商店和管理平台针对预装应用的政策会随时间变化,需按配置版本重新核查,不能沿用上一个项目的结论。
- 从不签入的设备会一直保持出厂时的版本;新发布的版本排着队却永远不会到达,而陈旧的最后在线时间戳看上去可能还像一台健康的设备。
在镜像冻结之前定下路径
修正交付决策最便宜的时刻是在配置版本冻结之前,因为在那之后,同样的修正就从一次发布变成了一次固件发版。请带着需求而不是偏好的机制前来:应用是否必须在无网络的首次开机时就存在、用户是否可以卸载、恢复出厂设置后是否必须回来、它需要做哪些普通应用做不到的事、它多久变更一次、由谁持有签名密钥,以及正在讨论的数量区间。Vantora 会把这些条件映射到一条交付路径和一个放置层级上,说明哪些部分是被强制执行的、哪些只是存在而已,在受版本控制的样机上确认该路径,并在做出任何承诺之前记录残留限制——把应用送上设备的各条预配路径见Android 设备预配方式,集成工作本身则属于应用、MDM 与 Kiosk 集成。
常见问题
用户可以卸载预装应用吗?
这完全取决于放置层级,正因如此,这个问题值得问得更精确。由被设为设备所有者的设备策略控制器强制安装的应用,只要该策略生效,其面向用户的卸载入口就被阻断。预装在只读系统分区上的应用根本不提供卸载——用户可以卸载后续更新回到出厂版本,是否能停用则取决于配置版本。从 OEM 预装区复制到数据区的应用,通常表现得与普通已安装应用一样、可以卸载,不过恢复出厂设置一般会将其恢复。以上每一种都取决于 OEM 和平台,需要在样机上从该配置留下的每一个可达入口尝试卸载来确认,而不能从路径名称推断。
恢复出厂设置之后,预装应用会怎样?
恢复出厂设置会擦除数据区,而系统分区保持不动,因此答案取决于放置方式。位于系统分区上的应用会回来,因为它从未被移除。在预配台上装入或由管理平台安装的应用不会自行回来——只有当预配路径重新运行、设备重新注册时它才会回来,因此重置场景应当作为一个有记录恢复时间的实测用例写进验收矩阵。从 OEM 预装区复制而来的应用通常会被重置恢复,但这一行为因 OEM 而异,需逐型号确认。在标准配置下,重置还会移除设备所有者,因此原本阻止卸载的策略也会随之消失,直到设备重新预配为止。
系统或固件更新之后,预装应用还在吗?
存在与否通常不受影响;需要重新核查的是行为。系统分区上的应用会被新镜像所携带的版本替换,因此原本通过某个渠道更新该应用的设备群,可能会发现底层的出厂版本换了。特权应用每个配置版本都要重新验证,因为它的权限白名单条目是随镜像一起走的,而在强制校验开启时,遗漏可能导致该版本无法启动。平台签名应用在每个配置版本上都必须重新签名并重新集成。即便是普通已安装应用,Android 版本变更也可能改变后台执行、权限和默认处理程序方面的行为。实务上的原则是:把任何固件变更都视为一次有指定责任人的重新验证触发点,而不是视为延续。
应用签名密钥应该由谁持有?
通常是软件的所有方,因为密钥决定了未来还能否发布:只有当新版本与已安装副本的签名身份相同时,平台才会接受它。需要及早定下来的一点是:交付去做预装的制品,必须就是更新渠道将要分发的那个已签名制品——同一份源码在本地签名的构建,在设备看来是另一个应用,渠道更新无法覆盖安装到它之上。如果应用商店代发布者管理签名密钥,那么应当预装的就是该商店签名的制品。保管问题还附带一条值得写下来的退出条款:如果商务关系终止,谁还能为已在现场的设备签名并发布版本。
预装应用能否在首次开机后自动启动?
自动启动在 MDM/EMM 中可以配置,对于放置在系统层的应用也可以在配置版本中安排,但它不是一项可以想当然的属性。Android 的后台执行与电池优化行为因版本和 OEM 而异,而且不少厂商在框架之上还叠加了自家的自启动管理。设备所有者配置比非受管配置提供更多选项——锁定任务或 Kiosk 配置可以让设备从开机起就停留在该应用内——而确切行为需在目标设备和配置版本上通过技术验证确认。请按实际使用方式测试:冷启动、电量耗尽关机后的启动,以及无网络可用时的启动。
应用可以离线更新吗?
可以安排支持离线的更新路径——在预配作业中装入安装包、在现场网络上设置本地更新源,或者对小规模设备群采用送回预配台的周期性做法——但由应用商店或管理平台驱动的更新,通常需要一条通往租户或商店的网络路径。从不联网的设备会一直保持出厂时的版本,而且不会主动报告自己已经过时,因此存在真正断网站点的设备群,需要约定离线更新窗口,并明确把新版本送上这些设备的方法。可行的做法应在验证期间针对具体部署确认,并写入验收记录,而不是从路径名称想当然地推断。