fdomain_client/
fidl.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::{
6    AnyHandle, AsHandleRef, Channel, ChannelMessageStream, ChannelWriter, Error, Handle,
7    HandleInfo, MessageBuf,
8};
9use fidl::epitaph::ChannelEpitaphExt;
10use fidl_fuchsia_fdomain as proto;
11use futures::{Stream, StreamExt, TryStream};
12use std::cell::RefCell;
13use std::marker::PhantomData;
14use std::sync::{Arc, Mutex};
15use std::task::Poll;
16
17pub trait FDomainFlexibleIntoResult<T> {
18    fn into_result_fdomain<P: ProtocolMarker>(
19        self,
20        method_name: &'static str,
21    ) -> Result<T, fidl::Error>;
22}
23
24impl<T> FDomainFlexibleIntoResult<T> for fidl::encoding::Flexible<T> {
25    fn into_result_fdomain<P: ProtocolMarker>(
26        self,
27        method_name: &'static str,
28    ) -> Result<T, fidl::Error> {
29        match self {
30            fidl::encoding::Flexible::Ok(ok) => Ok(ok),
31            fidl::encoding::Flexible::FrameworkErr(fidl::encoding::FrameworkErr::UnknownMethod) => {
32                Err(fidl::Error::UnsupportedMethod { method_name, protocol_name: P::DEBUG_NAME })
33            }
34        }
35    }
36}
37
38impl<T, E> FDomainFlexibleIntoResult<Result<T, E>> for fidl::encoding::FlexibleResult<T, E> {
39    fn into_result_fdomain<P: ProtocolMarker>(
40        self,
41        method_name: &'static str,
42    ) -> Result<Result<T, E>, fidl::Error> {
43        match self {
44            fidl::encoding::FlexibleResult::Ok(ok) => Ok(Ok(ok)),
45            fidl::encoding::FlexibleResult::DomainErr(err) => Ok(Err(err)),
46            fidl::encoding::FlexibleResult::FrameworkErr(
47                fidl::encoding::FrameworkErr::UnknownMethod,
48            ) => Err(fidl::Error::UnsupportedMethod { method_name, protocol_name: P::DEBUG_NAME }),
49        }
50    }
51}
52
53#[derive(Debug)]
54pub struct FDomainProxyChannel(Mutex<ChannelMessageStream>, ChannelWriter);
55
56impl FDomainProxyChannel {
57    pub fn on_closed(&self) -> crate::OnFDomainSignals {
58        self.1.as_channel().on_closed()
59    }
60
61    pub fn read_etc(
62        &self,
63        ctx: &mut std::task::Context<'_>,
64        bytes: &mut Vec<u8>,
65        handles: &mut Vec<HandleInfo>,
66    ) -> Poll<Result<(), Option<crate::Error>>> {
67        let Some(got) = std::task::ready!(self.0.lock().unwrap().poll_next_unpin(ctx)) else {
68            return Poll::Ready(Err(Some(Error::StreamingAborted)));
69        };
70
71        match got {
72            Ok(got) => {
73                *bytes = got.bytes;
74                *handles = got.handles;
75                Poll::Ready(Ok(()))
76            }
77            Err(Error::FDomain(proto::Error::TargetError(i)))
78                if i == fidl::Status::PEER_CLOSED.into_raw() =>
79            {
80                Poll::Ready(Err(None))
81            }
82            Err(e) => Poll::Ready(Err(Some(e))),
83        }
84    }
85}
86
87impl ::fidl::encoding::ProxyChannelBox<FDomainResourceDialect> for FDomainProxyChannel {
88    fn recv_etc_from(
89        &self,
90        ctx: &mut std::task::Context<'_>,
91        buf: &mut MessageBuf,
92    ) -> Poll<Result<(), Option<Error>>> {
93        let Some(got) = std::task::ready!(self.0.lock().unwrap().poll_next_unpin(ctx)) else {
94            return Poll::Ready(Err(Some(Error::StreamingAborted)));
95        };
96
97        match got {
98            Ok(got) => {
99                *buf = got;
100                Poll::Ready(Ok(()))
101            }
102            Err(Error::FDomain(proto::Error::TargetError(i)))
103                if i == fidl::Status::PEER_CLOSED.into_raw() =>
104            {
105                Poll::Ready(Err(None))
106            }
107            Err(e) => Poll::Ready(Err(Some(e))),
108        }
109    }
110
111    fn write_etc(&self, bytes: &[u8], handles: &mut [HandleInfo]) -> Result<(), Option<Error>> {
112        let mut handle_ops = Vec::new();
113        for handle in handles {
114            handle_ops.push(crate::channel::HandleOp::Move(
115                std::mem::replace(&mut handle.handle, AnyHandle::invalid()).into(),
116                handle.rights,
117            ));
118        }
119        let _ = self.1.fdomain_write_etc(bytes, handle_ops);
120        Ok(())
121    }
122
123    fn is_closed(&self) -> bool {
124        self.0.lock().unwrap().is_closed()
125    }
126
127    fn unbox(self) -> Channel {
128        self.0.into_inner().unwrap().rejoin(self.1)
129    }
130
131    fn as_channel(&self) -> &Channel {
132        self.1.as_channel()
133    }
134}
135
136#[derive(Debug, Copy, Clone, Default)]
137pub struct FDomainResourceDialect;
138impl ::fidl::encoding::ResourceDialect for FDomainResourceDialect {
139    type Handle = Handle;
140    type MessageBufEtc = MessageBuf;
141    type ProxyChannel = Channel;
142
143    #[inline]
144    fn with_tls_buf<R>(f: impl FnOnce(&mut ::fidl::encoding::TlsBuf<Self>) -> R) -> R {
145        thread_local!(static TLS_BUF: RefCell<::fidl::encoding::TlsBuf<FDomainResourceDialect>> =
146            RefCell::new(::fidl::encoding::TlsBuf::default()));
147        TLS_BUF.with(|buf| f(&mut buf.borrow_mut()))
148    }
149}
150
151impl ::fidl::encoding::MessageBufFor<FDomainResourceDialect> for MessageBuf {
152    fn new() -> MessageBuf {
153        MessageBuf { bytes: Vec::new(), handles: Vec::new() }
154    }
155
156    fn split_mut(&mut self) -> (&mut Vec<u8>, &mut Vec<HandleInfo>) {
157        (&mut self.bytes, &mut self.handles)
158    }
159}
160
161impl Into<::fidl::TransportError> for Error {
162    fn into(self) -> ::fidl::TransportError {
163        match self {
164            Error::FDomain(proto::Error::TargetError(i)) => {
165                ::fidl::TransportError::Status(fidl::Status::from_raw(i))
166            }
167            Error::SocketWrite(proto::WriteSocketError {
168                error: proto::Error::TargetError(i),
169                ..
170            }) => ::fidl::TransportError::Status(fidl::Status::from_raw(i)),
171            Error::ChannelWrite(proto::WriteChannelError::Error(proto::Error::TargetError(i))) => {
172                ::fidl::TransportError::Status(fidl::Status::from_raw(i))
173            }
174            Error::ChannelWrite(proto::WriteChannelError::OpErrors(ops)) => {
175                let Some(op) = ops.into_iter().find_map(|x| x) else {
176                    let err = Box::<dyn std::error::Error + Send + Sync>::from(
177                        "Channel write handle operation reported failure with no status!"
178                            .to_owned(),
179                    );
180                    return ::fidl::TransportError::Other(err.into());
181                };
182                let op = *op;
183                Error::FDomain(op).into()
184            }
185            other => ::fidl::TransportError::Other(std::sync::Arc::new(other)),
186        }
187    }
188}
189
190impl ::fidl::encoding::ProxyChannelFor<FDomainResourceDialect> for Channel {
191    type Boxed = FDomainProxyChannel;
192    type Error = Error;
193    type HandleDisposition = HandleInfo;
194
195    fn boxed(self) -> Self::Boxed {
196        let (a, b) = self.stream().unwrap();
197        FDomainProxyChannel(Mutex::new(a), b)
198    }
199
200    fn write_etc(&self, bytes: &[u8], handles: &mut [HandleInfo]) -> Result<(), Option<Error>> {
201        let mut handle_ops = Vec::new();
202        for handle in handles {
203            handle_ops.push(crate::channel::HandleOp::Move(
204                std::mem::replace(&mut handle.handle, AnyHandle::invalid()).into(),
205                handle.rights,
206            ));
207        }
208        let _ = self.fdomain_write_etc(bytes, handle_ops);
209        Ok(())
210    }
211}
212
213impl ::fidl::epitaph::ChannelLike for Channel {
214    fn write_epitaph(&self, bytes: &[u8]) -> Result<(), ::fidl::TransportError> {
215        let _ = self.write(bytes, vec![]);
216        Ok(())
217    }
218}
219
220impl ::fidl::encoding::HandleFor<FDomainResourceDialect> for Handle {
221    // This has to be static, so we can't encode a duplicate operation here
222    // anyway. So use HandleInfo.
223    type HandleInfo = HandleInfo;
224
225    fn invalid() -> Self {
226        Handle::invalid()
227    }
228
229    fn is_invalid(&self) -> bool {
230        self.client.upgrade().is_none()
231    }
232}
233
234impl ::fidl::encoding::HandleDispositionFor<FDomainResourceDialect> for HandleInfo {
235    fn from_handle(handle: Handle, object_type: fidl::ObjectType, rights: fidl::Rights) -> Self {
236        HandleInfo { handle: AnyHandle::from_handle(handle, object_type), rights }
237    }
238}
239
240impl ::fidl::encoding::HandleInfoFor<FDomainResourceDialect> for HandleInfo {
241    fn consume(
242        &mut self,
243        expected_object_type: fidl::ObjectType,
244        expected_rights: fidl::Rights,
245    ) -> Result<Handle, ::fidl::Error> {
246        let handle_info = std::mem::replace(
247            self,
248            HandleInfo {
249                handle: crate::AnyHandle::Unknown(Handle::invalid(), fidl::ObjectType::NONE),
250                rights: fidl::Rights::empty(),
251            },
252        );
253        let received_object_type = handle_info.handle.object_type();
254        if expected_object_type != fidl::ObjectType::NONE
255            && received_object_type != fidl::ObjectType::NONE
256            && expected_object_type != received_object_type
257        {
258            return Err(fidl::Error::IncorrectHandleSubtype {
259                // TODO: Find a way to put something better in here, either by
260                // expanding what FIDL can return or casting the protocol values
261                // to something FIDL can read.
262                expected: fidl::ObjectType::NONE,
263                received: fidl::ObjectType::NONE,
264            });
265        }
266
267        let received_rights = handle_info.rights;
268        if expected_rights != fidl::Rights::SAME_RIGHTS
269            && received_rights != fidl::Rights::SAME_RIGHTS
270            && expected_rights != received_rights
271        {
272            if !received_rights.contains(expected_rights) {
273                return Err(fidl::Error::MissingExpectedHandleRights {
274                    // TODO: As above, report something better here.
275                    missing_rights: fidl::Rights::empty(),
276                });
277            }
278
279            // TODO: The normal FIDL bindings call zx_handle_replace here to
280            // forcibly downgrade the handle rights. That's a whole IO operation
281            // for us so we won't bother, but maybe we should do something else?
282        }
283        Ok(handle_info.handle.into())
284    }
285
286    fn drop_in_place(&mut self) {
287        *self = HandleInfo {
288            handle: crate::AnyHandle::Unknown(Handle::invalid(), fidl::ObjectType::NONE),
289            rights: fidl::Rights::empty(),
290        };
291    }
292}
293
294impl ::fidl::encoding::EncodableAsHandle for crate::Event {
295    type Dialect = FDomainResourceDialect;
296}
297
298impl ::fidl::encoding::EncodableAsHandle for crate::EventPair {
299    type Dialect = FDomainResourceDialect;
300}
301
302impl ::fidl::encoding::EncodableAsHandle for crate::Socket {
303    type Dialect = FDomainResourceDialect;
304}
305
306impl ::fidl::encoding::EncodableAsHandle for crate::Channel {
307    type Dialect = FDomainResourceDialect;
308}
309
310impl ::fidl::encoding::EncodableAsHandle for crate::Handle {
311    type Dialect = FDomainResourceDialect;
312}
313
314impl<T: ProtocolMarker> ::fidl::encoding::EncodableAsHandle for ClientEnd<T> {
315    type Dialect = FDomainResourceDialect;
316}
317
318impl<T: ProtocolMarker> ::fidl::encoding::EncodableAsHandle for ServerEnd<T> {
319    type Dialect = FDomainResourceDialect;
320}
321
322/// Implementations of this trait can be used to manufacture instances of a FIDL
323/// protocol and get metadata about a particular protocol.
324pub trait ProtocolMarker: Sized + Send + Sync + 'static {
325    /// The type of the structure against which FIDL requests are made.
326    /// Queries made against the proxy are sent to the paired `ServerEnd`.
327    type Proxy: Proxy<Protocol = Self>;
328
329    /// The type of the stream of requests coming into a server.
330    type RequestStream: RequestStream<Protocol = Self>;
331
332    /// The name of the protocol suitable for debug purposes.
333    ///
334    /// For discoverable protocols, this should be identical to
335    /// `<Self as DiscoverableProtocolMarker>::PROTOCOL_NAME`.
336    const DEBUG_NAME: &'static str;
337}
338
339/// A marker for a particular FIDL protocol that is also discoverable.
340///
341/// Discoverable protocols may be referred to by a string name, and can be
342/// conveniently exported in a service directory via an entry of that name.
343///
344/// If you get an error about this trait not being implemented, you probably
345/// need to add the `@discoverable` attribute to the FIDL protocol, like this:
346///
347/// ```fidl
348/// @discoverable
349/// protocol MyProtocol { ... };
350/// ```
351pub trait DiscoverableProtocolMarker: ProtocolMarker {
352    /// The name of the protocol (to be used for service lookup and discovery).
353    const PROTOCOL_NAME: &'static str = <Self as ProtocolMarker>::DEBUG_NAME;
354}
355
356/// A type which allows querying a remote FIDL server over a channel.
357pub trait Proxy: Sized + Send + Sync {
358    /// The protocol which this `Proxy` controls.
359    type Protocol: ProtocolMarker<Proxy = Self>;
360
361    /// Create a proxy over the given channel.
362    fn from_channel(inner: Channel) -> Self;
363
364    /// Attempt to convert the proxy back into a channel.
365    ///
366    /// This will only succeed if there are no active clones of this proxy
367    /// and no currently-alive `EventStream` or response futures that came from
368    /// this proxy.
369    fn into_channel(self) -> Result<Channel, Self>;
370
371    /// Get a reference to the proxy's underlying channel.
372    ///
373    /// This should only be used for non-effectful operations. Reading or
374    /// writing to the channel is unsafe because the proxy assumes it has
375    /// exclusive control over these operations.
376    fn as_channel(&self) -> &Channel;
377
378    /// Get the client supporting this proxy. We call this a "domain" here because:
379    /// * Client is especially overloaded in contexts where this is useful.
380    /// * We simulate this call for target-side FIDL proxies, so it isn't always
381    ///   really a client.
382    fn domain(&self) -> Arc<crate::Client> {
383        self.as_channel().domain()
384    }
385}
386
387/// A stream of requests coming into a FIDL server over a channel.
388pub trait RequestStream: Sized + Send + Stream + TryStream<Error = fidl::Error> + Unpin {
389    /// The protocol which this `RequestStream` serves.
390    type Protocol: ProtocolMarker<RequestStream = Self>;
391
392    /// The control handle for this `RequestStream`.
393    type ControlHandle: ControlHandle;
394
395    /// Returns a copy of the `ControlHandle` for the given stream.
396    /// This handle can be used to send events or shut down the request stream.
397    fn control_handle(&self) -> Self::ControlHandle;
398
399    /// Create a request stream from the given channel.
400    fn from_channel(inner: Channel) -> Self;
401
402    /// Convert to a `ServeInner`
403    fn into_inner(self) -> (std::sync::Arc<fidl::ServeInner<FDomainResourceDialect>>, bool);
404
405    /// Convert from a `ServeInner`
406    fn from_inner(
407        inner: std::sync::Arc<fidl::ServeInner<FDomainResourceDialect>>,
408        is_terminated: bool,
409    ) -> Self;
410}
411
412/// A type associated with a `RequestStream` that can be used to send FIDL
413/// events or to shut down the request stream.
414pub trait ControlHandle {
415    /// Set the server to shutdown. The underlying channel is only closed the
416    /// next time the stream is polled.
417    fn shutdown(&self);
418
419    /// Returns true if the server has received the `PEER_CLOSED` signal.
420    fn is_closed(&self) -> bool;
421
422    /// Returns a future that completes when the server receives the
423    /// `PEER_CLOSED` signal.
424    fn on_closed(&self) -> crate::OnFDomainSignals;
425}
426
427/// A type associated with a particular two-way FIDL method, used by servers to
428/// send a response to the client.
429pub trait Responder {
430    /// The control handle for this protocol.
431    type ControlHandle: ControlHandle;
432
433    /// Returns the `ControlHandle` for this protocol.
434    fn control_handle(&self) -> &Self::ControlHandle;
435
436    /// Drops the responder without setting the channel to shutdown.
437    ///
438    /// This method shouldn't normally be used. Instead, send a response to
439    /// prevent the channel from shutting down.
440    fn drop_without_shutdown(self);
441}
442
443/// The Request type associated with a Marker.
444pub type Request<Marker> = <<Marker as ProtocolMarker>::RequestStream as futures::TryStream>::Ok;
445
446/// The `Client` end of a FIDL connection.
447#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
448pub struct ClientEnd<T: ProtocolMarker> {
449    inner: Channel,
450    phantom: PhantomData<T>,
451}
452
453impl<T: ProtocolMarker> ClientEnd<T> {
454    /// Create a new client from the provided channel.
455    pub fn new(inner: Channel) -> Self {
456        ClientEnd { inner, phantom: PhantomData }
457    }
458
459    /// Get a reference to the underlying channel
460    pub fn channel(&self) -> &Channel {
461        &self.inner
462    }
463
464    /// Extract the underlying channel.
465    pub fn into_channel(self) -> Channel {
466        self.inner
467    }
468}
469
470impl<'c, T: ProtocolMarker> ClientEnd<T> {
471    /// Convert the `ClientEnd` into a `Proxy` through which FIDL calls may be made.
472    pub fn into_proxy(self) -> T::Proxy {
473        T::Proxy::from_channel(self.inner)
474    }
475}
476
477impl<T: ProtocolMarker> From<ClientEnd<T>> for Handle {
478    fn from(client: ClientEnd<T>) -> Handle {
479        client.into_channel().into()
480    }
481}
482
483impl<T: ProtocolMarker> From<Handle> for ClientEnd<T> {
484    fn from(handle: Handle) -> Self {
485        ClientEnd { inner: handle.into(), phantom: PhantomData }
486    }
487}
488
489impl<T: ProtocolMarker> From<Channel> for ClientEnd<T> {
490    fn from(chan: Channel) -> Self {
491        ClientEnd { inner: chan, phantom: PhantomData }
492    }
493}
494
495impl<T: ProtocolMarker> AsHandleRef for ClientEnd<T> {
496    fn as_handle_ref(&self) -> crate::HandleRef<'_> {
497        AsHandleRef::as_handle_ref(&self.inner)
498    }
499
500    fn object_type() -> fidl::ObjectType {
501        <Channel as AsHandleRef>::object_type()
502    }
503}
504
505/// The `Server` end of a FIDL connection.
506#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
507pub struct ServerEnd<T: ProtocolMarker> {
508    inner: Channel,
509    phantom: PhantomData<T>,
510}
511
512impl<T: ProtocolMarker> ServerEnd<T> {
513    /// Create a new `ServerEnd` from the provided channel.
514    pub fn new(inner: Channel) -> ServerEnd<T> {
515        ServerEnd { inner, phantom: PhantomData }
516    }
517
518    /// Get a reference to the underlying channel
519    pub fn channel(&self) -> &Channel {
520        &self.inner
521    }
522
523    /// Extract the inner channel.
524    pub fn into_channel(self) -> Channel {
525        self.inner
526    }
527
528    /// Create a stream of requests off of the channel.
529    pub fn into_stream(self) -> T::RequestStream
530    where
531        T: ProtocolMarker,
532    {
533        T::RequestStream::from_channel(self.inner)
534    }
535
536    /// Create a stream of requests and an event-sending handle
537    /// from the channel.
538    pub fn into_stream_and_control_handle(
539        self,
540    ) -> (T::RequestStream, <T::RequestStream as RequestStream>::ControlHandle)
541    where
542        T: ProtocolMarker,
543    {
544        let stream = self.into_stream();
545        let control_handle = stream.control_handle();
546        (stream, control_handle)
547    }
548
549    /// Writes an epitaph into the underlying channel before closing it.
550    pub fn close_with_epitaph(self, status: fidl::Status) -> Result<(), fidl::Error> {
551        self.inner.close_with_epitaph(status)
552    }
553}
554
555impl<T: ProtocolMarker> From<ServerEnd<T>> for Handle {
556    fn from(server: ServerEnd<T>) -> Handle {
557        server.into_channel().into()
558    }
559}
560
561impl<T: ProtocolMarker> From<Handle> for ServerEnd<T> {
562    fn from(handle: Handle) -> Self {
563        ServerEnd { inner: handle.into(), phantom: PhantomData }
564    }
565}
566
567impl<T: ProtocolMarker> From<Channel> for ServerEnd<T> {
568    fn from(chan: Channel) -> Self {
569        ServerEnd { inner: chan, phantom: PhantomData }
570    }
571}
572
573impl<T: ProtocolMarker> AsHandleRef for ServerEnd<T> {
574    fn as_handle_ref(&self) -> crate::HandleRef<'_> {
575        AsHandleRef::as_handle_ref(&self.inner)
576    }
577
578    fn object_type() -> fidl::ObjectType {
579        <Channel as AsHandleRef>::object_type()
580    }
581}