blob: e029c50d058438dc03da1bb4b8df6da0d5bf78f0 [file] [log] [blame]
<html devsite><head>
<title>常见问题解答</title>
<meta name="project_path" value="/_project.yaml"/>
<meta name="book_path" value="/_book.yaml"/>
</head>
<body>
<!--
Copyright 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<h3>Google 是不是在所有设备上都采用了 A/B OTA?</h3>
<p>
是的。A/B 更新的营销名称是无缝更新。<em></em>从 2016 年 10 月份开始,Pixel 和 Pixel XL 手机在出厂时都具备 A/B 功能,并且所有 Chromebook 都使用相同的 <code>update_engine</code> A/B 实现。必要的平台代码实现在 Android 7.1 及更高版本中是公开的。
</p>
<h3>为什么 A/B OTA 更好?</h3>
<p>A/B OTA 能够为用户提供更好的更新体验。从每月安全更新数据中获得的指标显示,该功能非常成功:截至 2017 年 5 月,95% 的 Pixel 用户在一个月内采纳最新的安全更新,而 Nexus 用户则为 87%,并且 Pixel 用户执行更新的时间早于 Nexus 用户。如果在 OTA 期间无法成功更新数据块,将不会再导致设备无法启动;在新系统映像成功启动之前,Android 仍能够回退到上一个可使用的系统映像。</p>
<h3>A/B 更新对 2016 Pixel 分区大小有什么影响?</h3>
<p>下表包含推出的 A/B 配置与经过内部测试的非 A/B 配置的详细信息:</p>
<table>
<tbody>
<tr>
<th>Pixel 分区大小</th>
<th width="33%">A/B</th>
<th width="33%">非 A/B</th>
</tr>
<tr>
<td>引导加载程序</td>
<td>50*2</td>
<td>50</td>
</tr>
<tr>
<td>启动</td>
<td>32*2</td>
<td>32</td>
</tr>
<tr>
<td>恢复</td>
<td>0</td>
<td>32</td>
</tr>
<tr>
<td>缓存</td>
<td>0</td>
<td>100</td>
</tr>
<tr>
<td>无线通讯</td>
<td>70*2</td>
<td>70</td>
</tr>
<tr>
<td>供应商</td>
<td>300*2</td>
<td>300</td>
</tr>
<tr>
<td>系统</td>
<td>2048*2</td>
<td>4096</td>
</tr>
<tr>
<td><strong>总计</strong></td>
<td><strong>5000</strong></td>
<td><strong>4680</strong></td>
</tr>
</tbody>
</table>
<p>要进行 A/B 更新,只需要在闪存中增加 320MiB,而通过移除恢复分区,可节省 32MiB,并且通过移除缓存分区,可另外节省 100MiB。这将平衡引导加载程序、启动分区和无线通讯分区的 B 分区带来的开销。供应商分区增大了一倍(在增加的大小中占了绝大部分)。Pixel 的 A/B 系统映像大小是原来的非 A/B 系统映像的一半。
</p>
<p>对于经过内部测试的 Pixel A/B 和非 A/B 变体(仅推出了 A/B 变体),所用空间仅差 320MiB。在空间为 32GiB 的设备上,这只相当于不到 1%。对于空间为 16GiB 的设备,这相当于不到 2%,对于空间为 8GiB 的设备,这几乎相当于 4%(假设所有三种设备都具有相同的系统映像)。</p>
<h3>你们为何不使用 SquashFS?</h3>
<p>我们尝试过 SquashFS,但无法实现高端设备所需的性能。我们不会为手持设备使用 SquashFS,也不推荐这么做。</p>
<p>更具体地说,使用 SquashFS 时,系统分区上节省了约 50% 的大小,但绝大多数压缩率较高的文件都是预编译的 .odex 文件。这些文件都具有非常高的压缩比(接近 80%),但系统分区其余部分的压缩比要低得多。另外,SquashFS 在 Android 7.0 中引发了以下性能问题:</p>
<ul>
<li>与以往的设备相比,Pixel 具有非常快的闪存,但并没有大量的空闲 CPU 周期,因此虽然从闪存读取的字节数更少,但却需要更多的 CPU 来处理 I/O,这是一个潜在的制约因素。</li>
<li>在没有任何负载的系统上,有些 I/O 变化在人为基准条件下不会出现任何问题,但在具有真实负载(如 Nexus 6 上的加密)的实际用例中有时则会出现问题。</li>
<li>在某些方面,基准化分析显示回归率达到 85%。</li>
</ul>
<p>随着 SquashFS 日趋成熟并且增加了旨在降低 CPU 影响的功能(例如,将不应压缩且经常访问的文件列入白名单),我们将继续对其进行评估并向设备制造商提供建议。</p>
<h3>在不使用 SquashFS 的情况下,你们是如何做到将系统分区的大小减半的?</h3>
<p>应用存储在 .apk 文件中,这些文件实际上是 ZIP 档案。每个 .apk 文件中都有一个或多个包含可移植 Dalvik 字节码的 .dex 文件。.odex 文件(经过优化的 .dex 文件)会与 .apk 文件分开放置,并且可以包含特定于设备的机器代码。如果存在 .odex 文件,Android 将能够以预先编译的速度运行应用,而无需在每次启动应用时等待系统编译代码。.odex 文件并不是绝对必需的:实际上 Android 可以通过解译或即时 (JIT) 编译来直接运行 .dex 代码,但使用 .odex 文件可以实现启动速度和运行时速度的最佳组合(如果有足够的空间)。</p>
<p>示例:对于运行 Android 7.1 且系统映像总大小为 2628MiB(2755792836 字节)的 Nexus 6P 中的 installed-files.txt,在系统映像总大小中占据比重最大的几种文件类型明细如下:
</p>
<table>
<tbody>
<tr>
<td>.odex</td>
<td>1391770312 字节</td>
<td>50.5%</td>
</tr>
<tr>
<td>.apk</td>
<td>846878259 字节</td>
<td>30.7%</td>
</tr>
<tr>
<td>.so(原生 C/C++ 代码)</td>
<td>202162479 字节</td>
<td>7.3%</td>
</tr>
<tr>
<td>.oat 文件/.art 映像</td>
<td>163892188 字节</td>
<td>5.9%</td>
</tr>
<tr>
<td>字体</td>
<td>38952361 字节</td>
<td>1.4%</td>
</tr>
<tr>
<td>icu 语言区域数据</td>
<td>27468687 字节</td>
<td>0.9%</td>
</tr>
</tbody>
</table>
<p>这些数字在其他设备上是类似的,因此在 Nexus/Pixel 设备上,.odex 文件会占用系统分区大约一半的空间。这意味着我们可以继续使用 EXT4,但会在出厂前将 .odex 文件写入 B 分区,然后在第一次启动时将它们复制到 <code>/data</code>。用于 EXT4 A/B 的实际存储空间与用于 SquashFS A/B 的相同,因为如果我们使用了 SquashFS,我们会将经过预先优化的 .odex 文件放入 system_a 而非 system_b。</p>
<h3>将 .odex 文件复制到 /data 难道不是意味着在 /system 上节省的空间会在 /data 上被用掉吗?</h3>
<p>不完全是。在 Pixel 上,.odex 文件占用的大部分空间会用于应用(通常位于 <code>/data</code> 上)。这些应用通过 Google Play 更新,因此系统映像上的 .apk 和 .odex 文件在设备生命周期的大部分时间内都不会用到。当用户实际使用每个应用时,此类文件可以被完全排除并替换为由配置文件驱动的小型 .odex 文件(因此,用户不使用的应用不需要空间)。有关详细信息,请观看 Google I/O 2016 演讲 <a href="https://www.youtube.com/watch?v=fwMM6g7wpQ8">ART 的演变</a></p>
<p>难以进行比较的几个主要原因:</p>
<ul>
<li>由 Google Play 更新的应用在收到其第一次更新时,始终会立即将 .odex 文件放在 <code>/data</code> 上。</li>
<li>用户不运行的应用根本不需要 .odex 文件。</li>
<li>配置文件驱动型编译生成的 odex 文件比预先编译生成的 .odex 文件要小(因为前者仅会优化对性能至关重要的代码)。</li>
</ul>
<p>如需详细了解可供 OEM 使用的调整选项,请参阅<a href="/devices/tech/dalvik/configure.html">配置 ART</a></p>
<h3>.odex 文件在 /data 上不是有两个副本吗?</h3>
<p>这个问题有点复杂。写入新的系统映像后,系统将针对新的 .dex 文件运行新版本的 dex2oat,以生成新的 .odex 文件。这个过程发生在旧系统仍在运行时,因此旧的和新的 .odex 文件同时位于 <code>/data</code> 上。
</p>
<p>在优化每个软件包之前,OtaDexoptService 中的代码 (<code><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/OtaDexoptService.java#193">frameworks/base/+/master/services/core/java/com/android/server/pm/OtaDexoptService.java</a></code>) 都会调用 <code>getAvailableSpace</code>,以避免过度填充 <code>/data</code>。请注意,此处的可用数值仍然是保守估计:是指在达到通常的系统下限空间阈值之前剩余的空间量(以百分比和字节数计)。<em></em><em></em>所以如果 <code>/data</code> 已满,每个 .odex 文件便不会有两个副本。上述代码还有一个 BULK_DELETE_THRESHOLD:如果设备上的可用空间即将被填满(如上所述),则属于未使用应用的 .odex 文件将会被移除。这是每个 .odex 文件没有两个副本的另一种情况。</p>
<p>最糟糕的情况是 <code>/data</code> 已被完全填满,更新将等到设备重新启动到新系统,而不再需要旧系统的 .odex 文件时。PackageManager 可处理此情况:(<code><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageManagerService.java#7215" class="external">frameworks/base/+/master/services/core/java/com/android/server/pm/PackageManagerService.java#7215</a></code>)。在新系统成功启动之后,<code>installd</code> (<code><a href="https://android.googlesource.com/platform/frameworks/native/+/master/cmds/installd/dexopt.cpp#2422" class="external">frameworks/native/+/master/cmds/installd/dexopt.cpp#2422</a></code>) 可以移除旧系统之前用过的 .odex 文件,从而让设备返回到只有一个副本的稳定状态。</p>
<p>因此,尽管 <code>/data</code> 可能会包含所有 .odex 文件的两个副本,但 (a) 这是暂时的,并且 (b) 只有在 <code>/data</code> 上有足够的可用空间时才会发生。除了在更新期间,将始终只有一个副本。作为 ART 通用健壮性功能的一部分,它永远不会让 <code>/data</code> 中填满 .odex 文件(因为在非 A/B 系统上,这也会是一个问题)。</p>
<h3>这种写入/复制操作不会增加闪存磨损吗?</h3>
<p>只有一小部分闪存会被重写:完整 Pixel 系统更新会写入大约 2.3GiB 的数据。(应用也会被重新编译,但非 A/B 更新也是如此。)一直以来,基于块的完整 OTA 会写入类似数量的数据,所以闪存磨损率应该是类似的。</p>
<h3>刷写两个系统分区会增加出厂刷写时间吗?</h3>
<p>不会。Pixel 的系统映像大小并没有增加(只是将空间划分到了两个分区)。</p>
<h3>如果将 .odex 文件保留在 B 分区上,不会导致恢复出厂设置后重新启动速度变慢吗?</h3>
<p>会。如果您已实际使用了一台设备,进行了 OTA,并且执行了恢复出厂设置,则首次重新启动的速度将会比未进行这些操作时慢(在 Pixel XL 上,分别为 1 分 40 秒和 40 秒),因为在进行第一次 OTA 之后,B 中将会失去 .odex 文件,所以这些文件无法复制到 <code>/data</code>。正所谓有得有失。</p>
<p>与常规启动相比,恢复出厂设置应该是一项极少执行的操作,因此所花费的时间不是很重要。(这不会影响从工厂获取设备的用户或审核者,因为在这种情况下,B 分区可用。)使用 JIT 编译器意味着我们不需要重新编译所有内容,所以情况不会像您想象的那样糟糕。<em></em>此外,您也可以通过在清单 (<code>coreApp="true"</code>) 中使用 <code><a href="https://android.googlesource.com/platform/frameworks/base/+/master/packages/SystemUI/AndroidManifest.xml#23" class="external">frameworks/base/+/master/packages/SystemUI/AndroidManifest.xml#23</a></code> 将应用标记为需要预先编译。这是 <code>system_server</code> 当前采用的方式,因为出于安全考虑,不允许此进程进行 JIT 编译。</p>
<h3>如果将 .odex 文件保留在 /data 而非 /system 上,不会导致 OTA 后重新启动速度变慢吗?</h3>
<p>不会。如上所述,系统会在旧系统映像仍在运行时运行新的 dex2oat,以生成新系统将会需要的文件。在相关工作完成之前,系统会认为更新无法使用。</p>
<h3>我们可以(应该)推出 32GiB、16GiB 或 8GiB 的 A/B 设备吗?</h3>
<p>经证明,32GiB 空间在 Pixel 上能够很好地满足需求,而占用 16GiB 中的 320MiB 则意味着空间减少了 2%。同样,占用 8GiB 中的 320MiB 则意味着空间减少了 4%。显然,在空间为 4GiB 的设备上,不推荐使用 A/B 更新,因为 320MiB 的开销几乎占总可用空间的 10%。</p>
<h3>AVB2.0 需要 A/B OTA 吗?</h3>
<p>不需要。Android <a href="/security/verifiedboot/">验证启动</a>一直以来都是需要基于块的更新,但不一定是 A/B 更新。</p>
<h3>A/B OTA 需要 AVB2.0 吗?</h3>
<p>不需要。</p>
<h3>A/B OTA 会破坏 AVB2.0 的回滚保护吗?</h3>
<p>不会。这里存在一些混淆,因为如果 A/B 系统无法启动到新的系统映像,则会在重试一定的次数(由引导加载程序确定)后,自动恢复到“之前”的系统映像。但关键在于,对于使用 A/B 更新的系统而言,“之前”的系统映像实际上仍然是“当前”的系统映像。设备成功启动新映像后,回滚保护功能就会立即启动,以确保您无法再使用以前的系统启动。但是,在您实际成功启动新映像之前,回滚保护功能不会将其视为当前系统映像。</p>
<h3>如果在系统运行时安装更新,速度会不会很慢?</h3>
<p>使用非 A/B 更新时,目标是尽快安装更新,因为用户正在等待,并且在系统应用更新时,用户将无法使用其设备。使用 A/B 更新时,情况则恰恰相反。这是因为用户仍在使用其设备,于是目标就变成了尽可能减少影响,所以系统会有意缓慢地进行更新。通过 Java 系统更新客户端中的逻辑(对于 Google 来说是 GMSCore - 由 GMS 提供的核心程序包),Android 还会尝试选择用户完全不使用其设备的时间进行更新。该平台支持暂停/恢复更新,如果用户开始使用设备,客户端可以使用该功能来暂停更新,并在设备再次空闲时恢复更新。</p>
<p>在进行 OTA 时分两个阶段,这两个阶段在界面中的进度条下清楚地显示为“第 1 步(共 2 步)”和“第 2 步(共 2 步)”。<em></em><em></em>第 1 步是写入数据块,第 2 步是预编译 .dex 文件。这两个阶段在对性能的影响方面有很大差异。第一个阶段是简单的 I/O。这需要很少的资源(RAM、CPU、I/O),因为它只是缓慢地复制数据块。</p>
<p>第二个阶段是运行 dex2oat 来预编译新的系统映像。这显然在资源要求上没有明确的界限,因为它会编译实际应用。与编译小而简单的应用相比,编译大而复杂的应用所涉及的工作量显然要多出许多;而在第一个阶段,没有任何磁盘块会比其他磁盘块更大或更复杂。</p>
<p>该过程类似于 Google Play 先在后台安装应用更新,然后显示“已更新 5 个应用”通知,而这是多年来一直采用的做法。<em></em></p>
<h3>如果用户实际上正在等待更新,将会怎样?</h3>
<p>GmsCore 中的当前实现不区分后台更新和用户启动的更新,但将来可能会加以区分。届时,如果用户明确要求安装更新或正在查看更新进度屏幕,我们将假设他们正在等待系统完成更新,从而优先安排更新工作。</p>
<h3>如果无法应用更新,将会怎样?</h3>
<p>对于非 A/B 更新,如果更新无法应用,过去经常会导致用户的设备无法使用。唯一的例外情况是在尚未应用更新之前就出现问题(比如说因为更新包验证失败)。对于 A/B 更新,无法应用更新不会影响当前正在运行的系统。可以稍后重新尝试更新。</p>
<h3>哪些系统芯片 (SoC) 支持 A/B 更新?</h3>
<p>截至 2017 年 3 月 15 日,我们得到的信息如下:</p>
<table class="style0">
<tbody>
<tr>
<td></td>
<td><strong>Android 7.x 及更低版本</strong></td>
<td><strong>Android 8.x 及更高版本</strong></td>
</tr>
<tr>
<td><strong>Qualcomm</strong></td>
<td>根据 OEM 的请求而定</td>
<td>所有芯片组都将受支持</td>
</tr>
<tr>
<td><strong>Mediatek</strong></td>
<td>根据 OEM 的请求而定</td>
<td>所有芯片组都将受支持</td>
</tr>
</tbody>
</table>
<p>有关时间表的详细信息,请咨询您的 SoC 联系人。对于上面未列出的 SoC,请直接与您的 SoC 供应商联系。</p>
</body></html>