| <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/+/nougat-mr1-release/services/core/java/com/android/server/pm/OtaDexoptService.java#200" class="external">frameworks/base/+/nougat-mr1-release/services/core/java/com/android/server/pm/OtaDexoptService.java#200</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/+/nougat-mr1-release/services/core/java/com/android/server/pm/PackageManagerService.java#7215" class="external">frameworks/base/+/nougat-mr1-release/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/+/nougat-mr1-release/cmds/installd/commands.cpp#2192" class="external">frameworks/native/+/nougat-mr1-release/cmds/installd/commands.cpp#2192</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><a href="https://android.googlesource.com/platform/frameworks/base/+/nougat-mr1-release/packages/SystemUI/AndroidManifest.xml#23" class="external">frameworks/base/+/nougat-mr1-release/packages/SystemUI/AndroidManifest.xml#23</a></code>) 中使用 <code>coreApp="true"</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 来预编译新的系统映像。很显然,这在资源要求上没有明确的界限,因为它会编译实际应用。与编译简单的小应用相比,编译复杂的大应用所涉及的工作量显然要多出许多;而在第 1 阶段,没有任何磁盘块会比其他磁盘块更大或更复杂。</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> |