sandbox/fidl/
connector.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::fidl::registry;
6use crate::{Connector, ConversionError, Message, Receiver, WeakInstanceToken};
7use fidl::endpoints::ClientEnd;
8use fidl::handle::Channel;
9use futures::channel::mpsc;
10use std::sync::Arc;
11use vfs::directory::entry::DirectoryEntry;
12use vfs::execution_scope::ExecutionScope;
13use {fidl_fuchsia_component_sandbox as fsandbox, fuchsia_async as fasync};
14
15impl Connector {
16    pub(crate) fn send_channel(&self, channel: Channel) -> Result<(), ()> {
17        self.send(Message { channel })
18    }
19
20    pub(crate) fn new_with_fidl_receiver(
21        receiver_client: ClientEnd<fsandbox::ReceiverMarker>,
22        scope: &fasync::Scope,
23    ) -> Self {
24        let (sender, receiver) = mpsc::unbounded();
25        let receiver = Receiver::new(receiver);
26        // Exits when ServerEnd<Receiver> is closed
27        scope.spawn(receiver.handle_receiver(receiver_client.into_proxy()));
28        Self::new_sendable(sender)
29    }
30}
31
32impl crate::RemotableCapability for Connector {
33    fn try_into_directory_entry(
34        self,
35        _scope: ExecutionScope,
36        _token: WeakInstanceToken,
37    ) -> Result<Arc<dyn DirectoryEntry>, ConversionError> {
38        Ok(vfs::service::endpoint(move |_scope, server_end| {
39            let _ = self.send_channel(server_end.into_zx_channel().into());
40        }))
41    }
42}
43
44impl From<Connector> for fsandbox::Connector {
45    fn from(value: Connector) -> Self {
46        fsandbox::Connector { token: registry::insert_token(value.into()) }
47    }
48}
49
50impl crate::fidl::IntoFsandboxCapability for Connector {
51    fn into_fsandbox_capability(self, _token: WeakInstanceToken) -> fsandbox::Capability {
52        fsandbox::Capability::Connector(self.into())
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use assert_matches::assert_matches;
60    use fidl::endpoints::ClientEnd;
61    use fidl_fuchsia_io as fio;
62    use futures::StreamExt;
63    use vfs::ToObjectRequest;
64    use vfs::directory::entry::OpenRequest;
65    use vfs::execution_scope::ExecutionScope;
66
67    // TODO(340891837): This test only runs on host because of the reliance on Open
68    #[fuchsia::test]
69    async fn unwrap_server_end_or_serve_node_node_reference_and_describe() {
70        let receiver = {
71            let (receiver, sender) = Connector::new();
72            let open: crate::DirEntry = sender.into();
73            let (client, server) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
74            const FLAGS: fio::Flags =
75                fio::Flags::PROTOCOL_NODE.union(fio::Flags::FLAG_SEND_REPRESENTATION);
76            FLAGS.to_object_request(server.into_channel()).handle(|request| {
77                open.open_entry(OpenRequest::new(
78                    ExecutionScope::new(),
79                    FLAGS,
80                    vfs::Path::dot(),
81                    request,
82                ))
83            });
84
85            // The NODE_REFERENCE connection should be terminated on the sender side.
86            let result = client.take_event_stream().next().await.unwrap();
87            assert_matches!(
88                result,
89                Ok(fio::NodeEvent::OnRepresentation { payload: fio::Representation::Node(_) })
90            );
91
92            receiver
93        };
94
95        // After closing the sender, the receiver should be done.
96        assert_matches!(receiver.receive().await, None);
97    }
98
99    // TODO(340891837): This test only runs on host because of the reliance on Open
100    #[fuchsia::test]
101    async fn unwrap_server_end_or_serve_node_empty() {
102        let (receiver, sender) = Connector::new();
103        let open: crate::DirEntry = sender.into();
104
105        let (client_end, server_end) = Channel::create();
106        // The VFS should not send any event, but directly hand us the channel.
107        const FLAGS: fio::Flags = fio::Flags::PROTOCOL_SERVICE;
108        FLAGS.to_object_request(server_end).handle(|request| {
109            open.open_entry(OpenRequest::new(
110                ExecutionScope::new(),
111                FLAGS,
112                vfs::Path::dot(),
113                request,
114            ))
115        });
116        // Check that we got the channel.
117        assert_matches!(receiver.receive().await, Some(_));
118
119        // Check that there's no event, as we should be connected to the other end of the capability
120        // directly (i.e. this shouldn't actually be a fuchsia.io/Node connection).
121        let client_end: ClientEnd<fio::NodeMarker> = client_end.into();
122        let node: fio::NodeProxy = client_end.into_proxy();
123        assert_matches!(node.take_event_stream().next().await, None);
124    }
125}