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};
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    ) -> Result<Arc<dyn DirectoryEntry>, ConversionError> {
37        Ok(vfs::service::endpoint(move |_scope, server_end| {
38            let _ = self.send_channel(server_end.into_zx_channel().into());
39        }))
40    }
41}
42
43impl From<Connector> for fsandbox::Connector {
44    fn from(value: Connector) -> Self {
45        fsandbox::Connector { token: registry::insert_token(value.into()) }
46    }
47}
48
49impl From<Connector> for fsandbox::Capability {
50    fn from(connector: Connector) -> Self {
51        fsandbox::Capability::Connector(connector.into())
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use assert_matches::assert_matches;
59    use fidl::endpoints::ClientEnd;
60    use fidl_fuchsia_io as fio;
61    use futures::StreamExt;
62    use vfs::directory::entry::OpenRequest;
63    use vfs::execution_scope::ExecutionScope;
64    use vfs::ToObjectRequest;
65
66    // TODO(340891837): This test only runs on host because of the reliance on Open
67    #[fuchsia::test]
68    async fn unwrap_server_end_or_serve_node_node_reference_and_describe() {
69        let receiver = {
70            let (receiver, sender) = Connector::new();
71            let open: crate::DirEntry = sender.into();
72            let (client, server) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
73            const FLAGS: fio::Flags =
74                fio::Flags::PROTOCOL_NODE.union(fio::Flags::FLAG_SEND_REPRESENTATION);
75            FLAGS.to_object_request(server.into_channel()).handle(|request| {
76                open.open_entry(OpenRequest::new(
77                    ExecutionScope::new(),
78                    FLAGS,
79                    vfs::Path::dot(),
80                    request,
81                ))
82            });
83
84            // The NODE_REFERENCE connection should be terminated on the sender side.
85            let result = client.take_event_stream().next().await.unwrap();
86            assert_matches!(
87                result,
88                Ok(fio::NodeEvent::OnRepresentation { payload: fio::Representation::Node(_) })
89            );
90
91            receiver
92        };
93
94        // After closing the sender, the receiver should be done.
95        assert_matches!(receiver.receive().await, None);
96    }
97
98    // TODO(340891837): This test only runs on host because of the reliance on Open
99    #[fuchsia::test]
100    async fn unwrap_server_end_or_serve_node_empty() {
101        let (receiver, sender) = Connector::new();
102        let open: crate::DirEntry = sender.into();
103
104        let (client_end, server_end) = Channel::create();
105        // The VFS should not send any event, but directly hand us the channel.
106        const FLAGS: fio::Flags = fio::Flags::PROTOCOL_SERVICE;
107        FLAGS.to_object_request(server_end).handle(|request| {
108            open.open_entry(OpenRequest::new(
109                ExecutionScope::new(),
110                FLAGS,
111                vfs::Path::dot(),
112                request,
113            ))
114        });
115        // Check that we got the channel.
116        assert_matches!(receiver.receive().await, Some(_));
117
118        // Check that there's no event, as we should be connected to the other end of the capability
119        // directly (i.e. this shouldn't actually be a fuchsia.io/Node connection).
120        let client_end: ClientEnd<fio::NodeMarker> = client_end.into();
121        let node: fio::NodeProxy = client_end.into_proxy();
122        assert_matches!(node.take_event_stream().next().await, None);
123    }
124}