| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file |
| |
| import 'dart:async'; |
| |
| import 'package:fidl/fidl.dart'; |
| import 'package:fidl_fuchsia_io/fidl_async.dart'; |
| import 'package:zircon/zircon.dart'; |
| |
| /// Helper class to connect to incoming services. |
| /// |
| /// [Incoming] is used to connect to the services exposed by a launched component. |
| /// The general pattern for launching and connecting to a service is: |
| /// ``` |
| /// final incoming = Incoming(); |
| /// final launchInfo = LaunchInfo( |
| /// url: serverUrl, |
| /// directoryRequest: incoming.request().passChannel(), |
| /// ); |
| /// |
| /// final launcherProxy = LauncherProxy(); |
| /// context.incoming.connectToService(launcherProxy); |
| /// |
| /// launcherProxy.createComponent(launchInfo, ctrl); |
| /// final myProxy = MyProxy(); |
| /// incoming.connectToService(myProxy); |
| /// |
| /// incoming.close(); |
| /// ``` |
| /// |
| /// These services have been offered to this component by its parent or are |
| /// ambiently offered by the Component Framework. |
| class Incoming { |
| DirectoryProxy _dirProxy; |
| |
| /// Initializes [Incoming] with an unbound [DirectoryProxy] which can be used |
| /// to bind to a launched component's services. |
| Incoming() : this.withDirectory(DirectoryProxy()); |
| |
| /// Initializes [Incoming] with a [Directory] that should be bound to `/svc` |
| /// of this component. |
| /// |
| /// If you are launching a component use the [Incoming()] constructor |
| /// to get an unbound directory. |
| Incoming.withDirectory(this._dirProxy) : assert(_dirProxy != null); |
| |
| /// Terminates connection and return Zircon status. |
| Future<int> close() async { |
| return _dirProxy.close(); |
| } |
| |
| /// Connects to the incoming service specified by [serviceProxy]. |
| /// |
| /// If this object is not bound via the [request] method before |
| /// this method is called an [IncomingStateException] will be thrown. |
| void connectToService<T>(AsyncProxy<T> serviceProxy) { |
| if (serviceProxy == null) { |
| throw ArgumentError.notNull('serviceProxy'); |
| } |
| final String serviceName = serviceProxy.ctrl.$serviceName; |
| if (serviceName == null) { |
| throw Exception( |
| "${serviceProxy.ctrl.$interfaceName}'s controller.\$serviceName must " |
| 'not be null. Check the FIDL file for a missing [Discoverable]'); |
| } |
| |
| // Creates an interface request and binds one of the channels. Binding this |
| // channel prior to connecting to the agent allows the developer to make |
| // proxy calls without awaiting for the connection to actually establish. |
| final serviceProxyRequest = serviceProxy.ctrl.request(); |
| |
| connectToServiceByNameWithChannel( |
| serviceName, serviceProxyRequest.passChannel()); |
| } |
| |
| /// Connects to the incoming service specified by [serviceName] through the |
| /// [channel] endpoint supplied by the caller. |
| /// |
| /// If the service provider is not willing or able to provide the requested |
| /// service, it should close the [channel]. |
| /// |
| /// If this object is not bound via the [request] method before |
| /// this method is called an [IncomingStateException] will be thrown. |
| void connectToServiceByNameWithChannel(String serviceName, Channel channel) { |
| if (serviceName == null) { |
| throw Exception( |
| 'serviceName must not be null. Check the FIDL file for a missing ' |
| '[Discoverable]'); |
| } |
| if (channel == null) { |
| throw ArgumentError.notNull('channel'); |
| } |
| |
| if (_dirProxy.ctrl.isUnbound) { |
| throw IncomingStateException( |
| 'The directory must be bound before trying to connect to a service. ' |
| 'See [Incoming.request] for more information'); |
| } |
| |
| // connection flags for service: can read & write from target object. |
| const int _openFlags = openRightReadable | openRightWritable; |
| // 0755 |
| const int _openMode = 0x1ED; |
| |
| _dirProxy.open( |
| _openFlags, _openMode, serviceName, InterfaceRequest<Node>(channel)); |
| } |
| |
| /// Connects to the incoming service specified by [serviceProxy] through the |
| /// [channel] endpoint supplied by the caller. |
| /// |
| /// If the service provider is not willing or able to provide the requested |
| /// service, it should close the [channel]. |
| /// |
| /// If this object is not bound via the [request] method before |
| /// this method is called an [IncomingStateException] will be thrown. |
| void connectToServiceWithChannel<T>( |
| AsyncProxy<T> serviceProxy, Channel channel) { |
| if (serviceProxy == null) { |
| throw ArgumentError.notNull('serviceProxy'); |
| } |
| if (channel == null) { |
| throw ArgumentError.notNull('channel'); |
| } |
| final String serviceName = serviceProxy.ctrl.$serviceName; |
| if (serviceName == null) { |
| throw Exception( |
| "${serviceProxy.ctrl.$interfaceName}'s controller.\$serviceName must " |
| 'not be null. Check the FIDL file for a missing [Discoverable]'); |
| } |
| connectToServiceByNameWithChannel(serviceName, channel); |
| } |
| |
| /// Takes ownership of the Directory's request object for binding |
| /// to another processes outgoing services. |
| /// |
| /// The returned [InterfaceRequest] object is suitable to pass to |
| /// an object which can bind the directory. |
| /// |
| /// Subsequent calls to this method throw a FidlStateException |
| InterfaceRequest<Node> request() => |
| InterfaceRequest(_dirProxy.ctrl.request().passChannel()); |
| } |
| |
| /// An Exception that can be thrown if the [Incoming] object is in |
| /// a bad state. |
| class IncomingStateException implements Exception { |
| /// An error message describing the reason for this exception. |
| final String message; |
| |
| /// Creates a new instance of this. |
| IncomingStateException(this.message); |
| |
| @override |
| String toString() => 'IncomingStateException: $message'; |
| } |