字典功能

字典可將多項功能歸類為 將單一單位轉送為單一能力

字典的格式是鍵/值儲存庫,其中鍵是 capability name 字串,而其值是能力。 值本身也可以是另一項字典能力,可用來 或類似巢狀結構的巢狀結構

定義字典功能

如要定義新的字典,請為該字典新增 capability 宣告,如下所示:

capabilities: [
    {
        dictionary: "bundle",
    },
],

請參閱匯總一節,瞭解如何新增功能 然後提供給字典

從一開始,這個平台有一項重要區別 元件中的字典功能和大多數其他能力類型 例如通訊協定目錄。通訊協定能力最終是由 由元件相關聯的程式進行protocol 能力 宣告只是讓元件架構知道 以及通訊協定是否存在另一方面,dictionary 能力 由元件架構執行階段代管實際上,元件並不需要 具有 program 可宣告 dictionary 能力。

轉送字典功能

字典屬於集合類型,因此支援更多元的轉送方式 與其他能力類型相比

基本操作會公開字典給父項或 提供與其他功能類似。但 字典也支援以下額外的轉送作業:

  • 匯總:將功能安裝到已定義的字典中 這個元件
  • 巢狀結構:彙整字典中的字典,並使用 路徑語法,參照內部字典內的功能。
  • 擷取:從字典和路徑擷取能力 或單獨使用
  • 擴充功能:定義內含所有 另一項功能

字典建立後即無法修改。轉送字典授權 僅供讀取。可變動性說明瞭 字典的可變動性

如需更多有關能力轉送的一般資訊,請參閱 頂層網頁

公開

公開字典能力會授予元件的父項存取權:

{
    expose: [
        {
            dictionary: "bundle",
            from: "self",
        },
    ],
}

如同其他功能,您可以使用 as 關鍵字:

{
    expose: [
        {
            dictionary: "local-bundle",
            from: "self",
            as: "bundle",
        },
    ],
}

提供

提供字典能力可讓子項元件存取:

{
    offer: [
        {
            dictionary: "bundle",
            from: "parent",
            to: [ "#child-a", "#child-b" ],
        },
    ],
}

如同其他功能,您可以使用 as 變更子項顯示的名稱 關鍵字:

{
    offer: [
        {
            dictionary: "local-bundle",
            from: "self",
            to: [ "#child-a", "#child-b" ],
            as: "bundle",
        },
    ],
}

使用

這個架構目前不支援 usedictionary 能力設為 此類內容。不過,您可從多種位置擷取個別功能 並用來形容詞

匯總

如要在您定義的字典中新增功能,請使用 offer 並在 to 中指定目標字典。這項作業稱為 匯總。字典必須以 包含 offer

如要表示您想將能力新增至目標字典,請使用 以下是 to 中的語法:

to: "self/<dictionary-name>",

其中有 dictionary: "<dictionary-name>"capabilities 宣告。self/ 前置字串反映了字典的事實 就是這個元件的局部(這是字典路徑語法的其中一個案例) 相關說明請參閱擷取中所述)。

如同其他 offer 種類,在 您可以使用 as 關鍵字將字典從原始名稱變更為其他名稱。

以下是工作匯總的範例:

capabilities: [
    {
        dictionary: "bundle",
    },
    {
        directory: "fonts",
        rights: [ "r*" ],
        path: "/fonts",
    },
],
offer: [
    {
        protocol: "fuchsia.examples.Echo",
        from: "#echo-server",
        to: "self/bundle",
    },
    {
        directory: "fonts",
        from: "self",
        to: "self/bundle",
        as: "custom-fonts",
    },
],

建立巢狀結構

「匯總」的特殊案例是「能力」,其中 而是當做字典例如:

capabilities: [
    {
        dictionary: "bundle",
    },
],
offer: [
    {
        dictionary: "gfx",
        from: "parent",
        to: "self/bundle",
    },
],

如此一來,就可以將功能嵌入在更深層的字典中 第二,自訂角色只能 套用至專案或機構請參閱下一節,瞭解這項資訊。

擷取

從字典存取能力再獨立轉送功能, 稱為「擷取」

擷取作業有兩個輸入內容:用來擷取 能力,以及該字典中能力鍵的關鍵CML 代表 如下所示:

  • 字典路徑會在 from 屬性中提供。
    • 語法為 "<source>/<path>/<to>/<dictionary>"
    • <source> 可以是轉送作業的任一常見來源 支援。舉例來說,offer 支援 self#<child>parent, 且 expose 支援 self#<child>
    • 其餘路徑區段可識別 (可能為巢狀結構) 字典 已由 <source> 轉送至這個元件。
    • 將一般概念化為一般性 非巢狀 from 語法 (如範例所示 這裡)。從概念上說,您可以把 <source> 做為特殊的「頂層」字典提供的字典適用對象 例如,parent 是字典名稱,這個字典包含所有 父項提供的功能,#<child> 是字典 包含 <child> 公開的所有功能。如果您將 路徑包含額外線段,只是代表字典 巢狀結構中的上層帳戶
  • 能力命名的鍵將做為能力的值 關鍵字 (protocoldirectory 等)。這是 這與命名能力時,無法在 字典中。

這個語法最容易透過範例說明:

expose: [
    {
        protocol: "fuchsia.examples.Echo",
        from: "#echo-realm/bundle",
        to: "parent",
    },
],

在這個範例中,此元件預期名為 bundle 的字典為 由 #echo-realm 公開。from 包含這個字典的路徑: #echo-realm/bundlebundle 字典中的功能 要擷取及轉送的元件是具有索引鍵的 protocol fuchsia.examples.Echo。最後,這項通訊協定會向 元件的父項為 fuchsia.examples.Echo (做為個別能力,而非 附加至任何含有字典的一部分)。

類似的語法與 use 相容:

use: [
    {
        protocol: "fuchsia.examples.Echo",
        from: "parent/bundle",
    },
],

在這個例子中,元件預期父項會提供字典 bundle,包含通訊協定 fuchsia.examples.Echo。不是path 因此程式傳入命名空間中的通訊協定路徑 設為預設值/svc/fuchsia.examples.Echo

請注意,一般而言,使用宣告中的 from 預設值是 "parent",但因為我們是從提供的字典中擷取通訊協定 我們需要將父項明確指定為來源。

由於字典可「建立巢狀結構」,因此可擷取字典本身 並從字典中轉送:

offer: [
    {
        dictionary: "gfx",
        from: "parent/bundle",
        to: "#echo-child",
    },
],

在這個範例中,元件會假設父項為該元件提供字典 名為 bundle,這個 bundle 包含名為 gfx 的字典, 提供給「#echo-child」使用。

from 支援任意層級的巢狀結構。這是前一頁的變化 範例:

offer: [
    {
        protocol: "fuchsia.ui.Compositor",
        from: "parent/bundle/gfx",
        to: "#echo-child",
    },
],

如同最後一個範例,元件會假設父項提供字典 名為 bundle,而這個 bundle 包含名為 gfx 的字典。 最後,gfx 包含名為 fuchsia.ui.Compositor 的通訊協定能力。 是獨立提供給 #echo-child 使用。

最後,甚至可以結合 「匯總」,可將能力從一個字典轉送至 其他:

capabilities: [
    {
        dictionary: "my-bundle",
    },
],
offer: [
    {
        protocol: "fuchsia.examples.Echo",
        from: "parent/bundle",
        to: "self/my-bundle",
    },
],

擴充功能

在某些情況下,您可能會想建立一個涵蓋功能的字典 由多個元件新增無法以匯總的方式執行這項操作 一個字典,因為一個元件 可以在定義的字典中新增功能。

不過,您也可以透過擴充功能執行類似操作。擴充功能 作業可讓您宣告 複製自另一個字典 (稱為「來源字典」)。如此一來 可以建立新的字典,逐步使用先前的字典 而不必個別轉送所有功能

通常當您擴充字典時,還需要 擴充字典功能額外用於 功能不得與來源字典中的任何鍵衝突。如果他們 否則,當使用者嘗試擷取 來自擴充字典的能力

您可以新增 extends,將字典宣告為另一個字典 將關鍵字加入字典的 capability 宣告中,以識別來源 字典中。extends 的語法與先前提過的 from 語法相同 擷取

例如:

capabilities: [
    {
        dictionary: "my-bundle",
        extends: "parent/bundle",
    },
],
offer: [
    {
        protocol: "fuchsia.examples.Echo",
        from: "#echo-server",
        to: "self/my-bundle",
    },
    {
        dictionary: "my-bundle",
        from: "self",
        to: "#echo-client",
        as: "bundle",
    },
],

from 相同,extends 中的路徑可參照巢狀字典:

capabilities: [
    {
        dictionary: "my-gfx",
        extends: "parent/bundle/gfx",
    },
],

動態字典

在「擴充功能」的特殊情況下,來源字典可以是 元件程式在執行階段動態提供的字典。 這樣就能在執行階段佈建的字典與 宣告式 dictionary 功能。語法如下:

extends: "program/<outgoing-dir-path>",

<outgoing-dir-path> 是元件中的路徑 傳出目錄至處理常式 fuchsia.component.sandbox/Router 通訊協定 應傳回 Dictionary 能力。

讓我們透過範例說明這項功能。這個範例包含 分屬兩個元件

  • dynamic-dictionary-provider:建立執行階段 Dictionary 搭配三個 Echo 通訊協定 執行個體。這會透過 Router 公開 Dictionary 並在其 CML 中定義可擴充 Routerdictionary
  • dynamic-dictionary:宣告 CML 以擷取三個 來自 dictionaryEcho 通訊協定,並執行程式碼來使用這些 通訊協定
供應商

dynamic-dictionary-provider 的元件資訊清單如下所示。在這裡 請參閱擴充 Routerbundledictionary 定義。

    capabilities: [
        {
            dictionary: "bundle",
            path: "/svc/fuchsia.component.sandbox.Router",
        },
    ],
    use: [
        {
            protocol: [
                "fuchsia.component.sandbox.CapabilityStore",
                "fuchsia.component.sandbox.Factory",
            ],
            from: "framework",
        },
    ],
    expose: [
        {
            dictionary: "bundle",
            from: "self",
        },
    ],
}

在初始化時,dynamic-dictionary-provider 會使用 CapabilityStore sandbox API 建立新項目 Dictionary,然後新增三個 Connector。每項 Connector 代表 Echo 通訊協定執行個體的其中一個。

let store = client::connect_to_protocol::<fsandbox::CapabilityStoreMarker>().unwrap();
let id_gen = sandbox::CapabilityIdGenerator::new();

// Create a dictionary
let dict_id = id_gen.next();
store.dictionary_create(dict_id).await.unwrap().unwrap();

// Add 3 Echo servers to the dictionary
let mut receiver_tasks = fasync::TaskGroup::new();
for i in 1..=3 {
    let (receiver, receiver_stream) =
        endpoints::create_request_stream::<fsandbox::ReceiverMarker>().unwrap();
    let connector_id = id_gen.next();
    store.connector_create(connector_id, receiver).await.unwrap().unwrap();
    store
        .dictionary_insert(
            dict_id,
            &fsandbox::DictionaryItem {
                key: format!("fidl.examples.routing.echo.Echo-{i}"),
                value: connector_id,
            },
        )
        .await
        .unwrap()
        .unwrap();
    receiver_tasks.spawn(async move { handle_echo_receiver(i, receiver_stream).await });
}

每個 Connector 都會繫結至 ReceiverEcho 的傳入要求。Receiver 處理常式的實作方式為 與 ServiceFs 處理常式非常相似,差別在於前者是 ServiceFsReceiver 會繫結至 Connector,而不是 元件的傳出目錄

async fn handle_echo_receiver(index: u64, mut receiver_stream: fsandbox::ReceiverRequestStream) {
    let mut task_group = fasync::TaskGroup::new();
    while let Some(request) = receiver_stream.try_next().await.unwrap() {
        match request {
            fsandbox::ReceiverRequest::Receive { channel, control_handle: _ } => {
                task_group.spawn(async move {
                    let server_end = endpoints::ServerEnd::<EchoMarker>::new(channel.into());
                    run_echo_server(index, server_end.into_stream().unwrap()).await;
                });
            }
            fsandbox::ReceiverRequest::_UnknownMethod { ordinal, .. } => {
                warn!(%ordinal, "Unknown Receiver request");
            }
        }
    }
}

async fn run_echo_server(index: u64, mut stream: EchoRequestStream) {
    while let Ok(Some(event)) = stream.try_next().await {
        let EchoRequest::EchoString { value, responder } = event;
        let res = match value {
            Some(s) => responder.send(Some(&format!("{s} {index}"))),
            None => responder.send(None),
        };
        if let Err(err) = res {
            warn!(%err, "Failed to send echo response");
        }
    }
}

最後,我們需要公開先前建立的字典, Router

let mut fs = ServiceFs::new_local();
fs.dir("svc").add_fidl_service(IncomingRequest::Router);
fs.take_and_serve_directory_handle().unwrap();
fs.for_each_concurrent(None, move |request: IncomingRequest| {
    let store = store.clone();
    let id_gen = id_gen.clone();
    async move {
        match request {
            IncomingRequest::Router(mut stream) => {
                while let Ok(Some(request)) = stream.try_next().await {
                    match request {
                        fsandbox::RouterRequest::Route { payload: _, responder } => {
                            let dup_dict_id = id_gen.next();
                            store.duplicate(dict_id, dup_dict_id).await.unwrap().unwrap();
                            let capability = store.export(dup_dict_id).await.unwrap().unwrap();
                            let _ = responder.send(Ok(capability));
                        }
                        fsandbox::RouterRequest::_UnknownMethod { ordinal, .. } => {
                            warn!(%ordinal, "Unknown Router request");
                        }
                    }
                }
            }
        }
    }
})
.await;

Router 要求處理常式會匯出字典, 傳回。請注意 字典的 CapabilityStore/Duplicate 因為架構可能會多次呼叫 Router/Route這個引數包含 複製字典把手的效果,不複製內部 內容。

fsandbox::RouterRequest::Route { payload: _, responder } => {
    let dup_dict_id = id_gen.next();
    store.duplicate(dict_id, dup_dict_id).await.unwrap().unwrap();
    let capability = store.export(dup_dict_id).await.unwrap().unwrap();
    let _ = responder.send(Ok(capability));
}
用戶端

用戶端很簡單。首先,元件資訊清單會擷取 來自 bundle 字典的通訊協定 (使用一般擷取) 語法。

use: [
    {
        protocol: [
            "fidl.examples.routing.echo.Echo-1",
            "fidl.examples.routing.echo.Echo-2",
            "fidl.examples.routing.echo.Echo-3",
        ],
        from: "#provider/bundle",
    },
],

程式會依序連線到每個通訊協定並嘗試使用:

for i in 1..=3 {
    info!("Connecting to Echo protocol {i} of 3");
    let echo = client::connect_to_protocol_at_path::<EchoMarker>(&format!(
        "/svc/fidl.examples.routing.echo.Echo-{i}"
    ))
    .unwrap();
    let res = echo.echo_string(Some(&format!("hello"))).await;
    assert_matches!(res, Ok(Some(s)) if s == format!("hello {i}"));
}

可變動性

dictionary 功能遵循下列可變動規則:

  • 只有定義字典的元件允許 匯總擴充資料。此外,這是 將功能新增至 dictionary 的方式,而且無法 而且在執行階段修改檔案
  • 轉送字典的元件皆可擷取 所有 AI 功能
  • 如果元件發出兩個連續的轉送要求, 從相同的 dictionary 擷取,則每個要求可能會 會傳回不同的結果。舉例來說,雖然在 其他失敗。這是能力要求性質的副作用 轉送。在兩項要求之間,可能 上游重新解決,並將 字典