RFC-0155:可选的功能路由

RFC-0155:可选功能路由
状态已接受
区域
  • 组件框架
说明

在组件清单的提供和使用部分添加了一个新字段,用于表示给定的功能可能不存在的时间。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-02-07
审核日期(年-月-日)2022-04-14

摘要

在组件清单的提供和使用部分添加了一个新字段,用于表示给定的功能可能不存在。

设计初衷

核心 Realm 分片

使用核心 Realm 分片时,会出现功能路由的来源或目标不存在的情况。例如,

  • 组件 /core/wlancfg 使用协议 fuchsia.location.sensor.WlanBaseStationWatcher,但某些产品上不存在此协议。
  • 组件 /core/bt-a2dp 必须使用 fuchsia.power.battery.BatteryManager 功能(如果可用),但并非所有产品都提供此功能。
  • /core/omaha-client-service 组件必须使用 fuchsia.update.config.OptOut 协议(如果可用),但并非所有产品都支持该协议。
  • CommsAgent 软件包必须使用视频(如果有),但某些产品上没有视频。
  • 轨迹提供程序是提供 fuchsia.tracing.provider.Registry 功能的组件。这对于开发目的很有用,但不应包含在用户 build 中。

上述场景是我们希望在未来支持的组件配置,但目前组件框架中有几个地方存在与此相关的粗糙边缘:

  • 如果向清单中不存在的组件提供或从清单中不存在的组件获取服务,会导致清单验证失败。
  • 如果组件未公开某项功能,但您却提供了该功能,则会导致 scrutiny 验证失败。
  • 如果功能路由失败(例如,尝试使用未提供的功能),组件管理器会(正确地)发出警告级日志,这在调查有意省略相应功能的产品问题时可能会令人担忧,从而造成误导。

通过集合进行路线验证

Scrutiny 是一种组件路由验证工具,可确保给定组件树中的所有已用功能都具有有效的功能路由。除了它提供的系统正确性断言之外,它还是一种有用的开发工具,因为它可以将路由验证从运行时移至 build 时,从而缩短开发周期。

如今,我们维护着一个许可名单,用于停用对特定功能路由的检查。这样一来,当组件使用不可用的功能时,build 可以成功完成,但不幸的是,当功能路由在功能可用的 build 上配置错误时,build 也可以成功完成。

此外,一旦 https://fxbug.dev/42174612 得到解决,审查还将验证源自会话 realm 的功能路由。会话组件通常依赖于源自会话外部的功能,如果其中一个组件希望添加到审查许可名单中,则会增加摩擦,因为这些组件可能是在 fuchsia.git 之外开发的。

利益相关方

哪些人会受到此 RFC 是否被接受的影响?(此部分为可选,但建议填写。)

教员:Hunter Freyer (hjfreyer@google.com)

由 FEC 指派负责引导此 RFC 完成 RFC 流程的人员。

审核者

  • Mark Dittmer (markdittmer@google.com) - 安全
  • Gary Bressler (geb@google.com) - 组件框架
  • Marc Khouri (mnck@google.com) - WLAN
  • Ani Ramakrishnan (aniramakri@google.com) - 蓝牙
  • Yaar Schnitman (yaar@google.com) - 产品

共同化

在收到 https://fxbug.dev/42168255 的要求并征求了一些特定个人的反馈后,此 RFC 的早期版本已在利益相关者之间共享。

设计

系统将添加一个名为 availability 的新字段,以用于 use 声明。此字段的默认值为 required,这意味着组件需要提供相应的功能,如果该功能不可用,则可能会出现故障。此 RFC 未提议对 required 功能的行为进行任何更改 - 它们将像所有功能一样运行

{
    use: [
        {
            protocol: "fuchsia.logger.LogSink",
            availability: "required",
        },
        {
            directory: "config-data",
            path: "/config",
            rights: [ "r*" ],
            // The `availability` field defaults to `required` when omitted.
        },
    ],
}

use 声明中的 availability 字段也可以设置为 optional,以反映当 capability 不可用时组件何时可以正常运行(可能具有修改后的行为)。

{
    use: [
        {
            // Emergency location is not present in all products.
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            availability: "optional",
        },
    ],
}

系统会将名为 void 的新来源添加到商品声明的可能来源列表中。

{
    offer: [
        {
            protocol: "fuchsia.update.config.OptOut",
            from: "void",
        },
    ],
}

使用声明为可选的 capability 可以来自现有组件,也可以来自“void”。需要使用声明的功能可能不会从 void 提供,因为这可能会导致组件出现故障。

中间优惠(从父级到子级)也可以将 availability 字段设置为以下值之一:

  • required(默认):无论子级是否可以处理此功能缺失的情况,子级必须获得此功能的访问权限(祖先中可能没有提供此功能)。
  • optional:子级必须能够处理缺少此功能的情况(优惠链末尾的使用声明必须是可选的)。
  • same_as_target:相应优惠将具有与目标相同的可选性。如果目标需要相应功能,则此优惠规定目标必须接收相应功能。如果目标对相应功能有可选用途,则此 offer 规定目标可能接收或不接收此功能。
{
    offer: [
        {
            protocol: "fuchsia.power.battery.BatterManager",
            from: "parent",
            to: "bt-a2dp",
            // Emit a build-time error if this protocol is not correctly routed
            // to this child (including offers from void).
            availability: "required",
        },
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            from: "parent",
            to: "wlancfg",
            // Emit a build-time error if this child is unable to handle the
            // absence of this protocol.
            availability: "required",
        },
    ],
}

如果某个组件具有 optional 用途,而其父组件以 required 形式提供相应功能,那么如果该功能的路由无效或以“来自 void 的提供”结尾,系统将发出 build-time 错误。如果某个组件具有 required 用途,而其父组件以 optional 形式提供相应功能,系统将发出 build-time 错误。此字段可能未设置,在这种情况下,系统会忽略它。

核心 Realm 程序集

RFC-0089 中所述,核心 realm 由“核心 shard”组装而成,这些 shard 是在 build 时与核心 realm 合并的 CML 代码段。核心分片的确切集合由产品定义设置。

一般建议将面向儿童的优惠与儿童声明包含在同一核心分片中。这有助于确保,如果 build 中包含子 build,那么子 build 正常运行所需的权能也会提供给它。

如果优惠希望具有也可选择性地包含在 build 中的来源,那么将儿童优惠与儿童包含在同一分片中会变得很复杂。如果以子 a 为目标的 offer 具有子 b 的来源,但 b 也位于分片中,则 a 的分片只能包含在也包含 b 的分片的 build 中,否则由于来自不存在的子级的 offer,系统会在 build 时发出清单验证错误。

为了让儿童的商品与儿童的声明位于同一核心分片中(无论来源如何),我们将在商品声明中引入一个名为 source_availability 的新字段。此字段的默认值为 present,但可以设置为 unknown

source_availability 设置为 unknown 时,如果优惠声明引用了清单中不存在的来源,则该声明将通过清单验证。在清单编译期间,缺失的 offer 声明来源将被替换为 void 来源,从而允许任何以该 offer 声明结尾的 use 声明通过将其可用性设置为 optional 来通过路由验证,以反映相应功能并非在所有产品上都存在。

{
    offer: [ {
        protocol: "fuchsia.examples.Echo",
        from: "#child-that-might-not-be-declared",
        to: "#echo-user",
        source_availability: "unknown",
    } ],
}

这样,平台配置维护人员就可以依赖核心 shard 集作为核心 realm 中存在哪些功能和组件的唯一可靠来源。省略核心分片不仅会从核心 realm 中移除子系统,还会将任何以该子系统为来源的 offer 准确更新为来自 void 的 offer,以便任何对该子系统具有可选使用情况的组件在有意从系统中排除这些子系统时,不会再收到对这些子系统的访问权限(而任何必需的使用情况仍会在这种情况下导致错误)。

示例

核心 realm 中具有可选功能的组件

在此示例中,我们来看看 wlancfg 的清单和核心 Realm 分片会根据提议的设计发生哪些变化。

fuchsia.location.sensor.WlanBaseStationWatcher 协议的 offer 将从 emergency.core_shard.cml 移至 wlancfg.core_shard.cml,并且 source_availability 字段设置为 unknown(因为紧急核心 shard 以及紧急组件可能包含在与 wlancfg 相同的平台配置中,也可能不包含在其中)。

// src/connectivity/location/emergency/meta/emergency.core_shard.cml
{
    offer: [
        // This is removed
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            from: "#emergency",
            to: "#wlancfg",
        },
    ],
}
// src/connectivity/wlan/wlancfg/meta/wlancfg.core_shard.cml
{
    offer: [
        // This is added
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            from: "#emergency",
            to: "#wlancfg",
            source_availability: "unknown",
        },
    ],
}

将优惠声明移至核心分片(针对 wlancfg)是 source_availability 字段正常运行所必需的,并且也符合核心分片的最佳实践。

除了核心分片更改之外,wlancfg 中此协议的使用声明也会更新为 optional

// src/connectivity/wlan/wlancfg/meta/wlancfg.cml
{
    use: [
        {
            protocol: "fuchsia.location.sensor.WlanBaseStationWatcher",
            // This line is added
            availability: "optional",
        },
    ],
}

现在,如果构建中不包含 emergency.core_shard.cml 文件,则不会因 wlancfg 无法访问 fuchsia.location.sensor.WlanBaseStationWatcher 协议而出现构建时错误。这意味着该协议可能会从审查的许可名单中移除,并且任何导致该协议在可用的平台配置中对 wlancfg 不可用的配置错误都会导致 build 时错误。

会话中具有可选功能的组件

在此示例中,我们来看看根据提议的设计,获取 fuchsia.power.battery.BatteryManager 协议(从 /core/battery_manager/core/session_manager/session:session/workstation_session/login_shell/ermine_shell)所涉及的清单和核心 Realm 分片会发生哪些变化。

workstation.core_shard.cml 文件中针对相应协议的 offer 将更新为将 source_availability 设置为 unknown

// src/session/bin/session_manager/meta/workstation.core_shard.cml
{
    offer: [
            protocol: "fuchsia.power.battery.BatteryManager",
            from: "#battery_manager",
            to: "#session-manager",
            // This line is added
            source_availability: "unknown",
    ],
}

coreermine_shell 之间的优惠无需更改,并且 ermine_shell 清单会更新为将 availability 字段设置为 optional(对于协议):

// src/experiences/session_shells/ermine/shell/meta/ermine.cml
{
    use: [
        {
            protocol: "fuchsia.power.battery.BatteryManager",
            availability: "optional",
        },
    ],
}

进行这些更改后,workstation.core.shard.cml 文件可以包含在不含 battery_manager.core_shard.cml 的平台配置中,而不会导致构建时审查错误,也不会将此功能添加到许可名单中。这意味着,在旨在通过不含电池的硬件运行工作站会话的平台配置中,可以安全地排除电池管理器组件。

会话所有者希望确保可选功能始终

可用

在前面的示例基础上,假设有一个名为 workstation-on-laptops 的虚构会话,该会话与 workstation 会话相同,只是它具有额外的笔记本电脑专用功能。如果此会话的所有者希望使用 workstation 会话中存在的同一 ermine 组件,但希望确保该组件始终有权访问 fuchsia.power.battery.BatteryManager 协议(通过构建时错误强制执行),则会话所有者可将此协议作为 required 提供给 ermine

// src/experiences/session_shells/ermine/session/meta/workstation_on_laptops_session.cml
{
    offer: [
            protocol: "fuchsia.power.battery.BatteryManager",
            from: "parent",
            // login_shell offers the capability to ermine
            to: "#login_shell",
            availability: "required",
    ],
}

在此示例中,如果将 workstation-on-laptops 会话与提供来自 voidfuchsia.power.battery.BatteryManager 协议的平台配置(即不包含 battery_manager.core_shard.cml)进行比较,则即使 ermine.cml 对该协议具有可选用途,也会发出 build 时错误。

会话所有者希望确保组件能够处理

可选功能

再次以“ermine 可选地使用 BatteryManager”为例,如果工作站会话的所有者希望确保 ermine 始终能够处理 fuchsia.power.battery.BatteryManager 协议的缺失情况,无论该协议的当前可用性如何,他们都可以将该协议作为 optional 提供给 ermine

// src/experiences/session_shells/ermine/session/meta/workstation_session.cml
{
    offer: [
            protocol: "fuchsia.power.battery.BatteryManager",
            from: "parent",
            // login_shell offers the capability to ermine
            to: "#login_shell",
            availability: "optional",
    ],
}

此可选功能不会影响给定安排的 build 或运行时行为,因为 ermine 也具有可选的功能使用。不过,如果 ermine 发生更改,需要此功能,那么无论功能路由是否有效或是否以“来自 void 的 offer”结尾,scrutiny 都会发出 build-time 错误。

实现

为了实现这些更改,必须更新 fuchsia.component.decl.Component FIDL 表和 CML JSON 架构以包含新字段,同时更新 CMC、cm_fidl_validator 和 Scrutiny 以正确处理设计部分中描述的 optional 语义。

性能

建议的更改不会对 build 时间或大小产生任何重大影响,因为在这些清单中携带额外的枚举的成本非常小。此外,组件管理器中提议的运行时行为变更不会对性能产生任何重大影响,因为在开始这项工作时,实现新命名空间程序集逻辑所需的信息全部都在内存中。

工效学设计

对于目前需要与许可名单交互来抑制审查验证错误的任何组件作者来说,此更改应能显著改善人体工程学,因为预期验证错误的相关上下文将得到改进,这样审查就不会再发出这些预期错误。

向后兼容性

此更改纯粹是对 FIDL 表和 JSON 格式的添加,因此不存在向后兼容性问题。

安全注意事项

这项变更将使组件作者能够停用未路由功能的 Scrutiny 错误,而无需获得安全团队的批准,因为不再使用许可名单来控制未路由功能的 Scrutiny 错误抑制。

之所以认为此提案可以接受,是因为创建或更改组件路由本身的审核流程并未因该提案而发生变化。

隐私注意事项

拟议的变更不会影响用户数据的收集、处理或存储方式,因此不会引发任何隐私权问题。

测试

目前已有大量测试涵盖组件清单流水线。 这些测试将进行更改,以涵盖这项新功能。

文档

组件清单文档将更新,以说明新字段及其对验证的影响。

缺点、替代方案和未知因素

在先技术和参考资料

TODO