| // Copyright 2018 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_modular/fidl.dart' as fidl; |
| import 'package:fidl_fuchsia_sys/fidl.dart' as fidl; |
| import 'package:lib.app.dart/logging.dart'; |
| import 'package:lib.component.dart/component.dart'; |
| import 'package:lib.entity.dart/entity.dart'; |
| import 'package:meta/meta.dart'; |
| |
| /// A client wrapper for [fidl.ComponentContext]. |
| class ComponentContextClient { |
| /// The underlying [Proxy] used to send client requests to the |
| /// [fidl.ComponentContextProxy] service. |
| final fidl.ComponentContextProxy proxy = fidl.ComponentContextProxy(); |
| |
| final EntityResolverClient _entityResolver = EntityResolverClient(); |
| |
| // Keep track of agent controllers created to close the channels onTerminate |
| final List<fidl.AgentControllerProxy> _agentControllers = |
| <fidl.AgentControllerProxy>[]; |
| |
| /// Constructor. |
| ComponentContextClient() { |
| proxy.ctrl |
| ..onBind = _handleBind |
| ..onClose = _handleClose |
| ..onConnectionError = _handleConnectionError |
| ..onUnbind = _handleUnbind; |
| } |
| |
| /// A future that completes when the [proxy] is bound. |
| Future<Null> get bound => _bind.future; |
| final Completer<Null> _bind = Completer<Null>(); |
| |
| /// See [fidl.ComponentContext#createEntityWithData]. |
| Future<String> createEntityWithData(List<fidl.TypeToDataEntry> typeToData) { |
| Completer<String> completer = Completer<String>(); |
| |
| // ignore: unawaited_futures |
| proxy.ctrl.error.then((ProxyError err) { |
| if (!completer.isCompleted) { |
| completer.completeError(err); |
| } |
| }); |
| |
| void onSuccess(String value) { |
| if (!completer.isCompleted) { |
| if (value != null) { |
| completer.complete(value); |
| } else { |
| completer.completeError(Exception('entity reference is null')); |
| } |
| } |
| } |
| |
| try { |
| proxy.createEntityWithData(typeToData, onSuccess); |
| } on Exception catch (err, stackTrace) { |
| completer.completeError(err, stackTrace); |
| } |
| |
| return completer.future; |
| } |
| |
| /// See [fidl.ComponentContext#getEntityResolver]. |
| Future<EntityResolverClient> getEntityResolver() async { |
| if (_entityResolver.proxy.ctrl.isBound) { |
| return _entityResolver; |
| } |
| |
| Completer<EntityResolverClient> completer = |
| Completer<EntityResolverClient>(); |
| |
| try { |
| await bound; |
| } on Exception catch (err, stackTrace) { |
| completer.completeError(err, stackTrace); |
| } |
| |
| // ignore: unawaited_futures |
| proxy.ctrl.error.then((ProxyError err) { |
| if (!completer.isCompleted) { |
| completer.completeError(err); |
| } |
| }); |
| |
| // ignore: unawaited_futures |
| _entityResolver.proxy.ctrl.error.then((ProxyError err) { |
| if (!completer.isCompleted) { |
| completer.completeError(err); |
| } |
| }); |
| |
| try { |
| proxy.getEntityResolver(_entityResolver.proxy.ctrl.request()); |
| } on Exception catch (err, stackTrace) { |
| completer.completeError(err, stackTrace); |
| } |
| |
| scheduleMicrotask(() { |
| if (!completer.isCompleted) { |
| completer.complete(_entityResolver); |
| } |
| }); |
| |
| return completer.future; |
| } |
| |
| /// Obtain a named message queue for receiving messages. |
| MessageQueueClient obtainMessageQueue( |
| {@required String name, |
| @required MessageReceiverCallback onMessage, |
| @required MessageQueueErrorCallback onConnectionError}) { |
| var mq = MessageQueueClient( |
| onMessage: onMessage, onConnectionError: onConnectionError); |
| proxy.obtainMessageQueue(name, mq.newRequest()); |
| return mq; |
| } |
| |
| /// Obtain a message queue sender from a token. |
| MessageSenderClient getMessageSender( |
| {@required String queueToken, |
| @required MessageSenderErrorCallback onConnectionError}) { |
| var sender = MessageSenderClient(onConnectionError: onConnectionError); |
| proxy.getMessageSender(queueToken, sender.newRequest()); |
| return sender; |
| } |
| |
| /// Connect to an agent |
| Future<fidl.ServiceProviderProxy> connectToAgent(String url) { |
| Completer<fidl.ServiceProviderProxy> serviceCompleter = |
| Completer<fidl.ServiceProviderProxy>(); |
| |
| // Connect to the agent and save off the agent controller proxy to be |
| // closed on terminate |
| fidl.ServiceProviderProxy serviceProviderProxy = |
| fidl.ServiceProviderProxy(); |
| serviceProviderProxy.ctrl.error.then((ProxyError err) { |
| if (!serviceCompleter.isCompleted) { |
| serviceCompleter.completeError(err); |
| } |
| }); |
| |
| fidl.AgentControllerProxy agentControllerProxy = |
| fidl.AgentControllerProxy(); |
| _agentControllers.add(agentControllerProxy); |
| agentControllerProxy.ctrl.error.then((ProxyError err) { |
| if (!serviceCompleter.isCompleted) { |
| serviceCompleter.completeError(err); |
| } |
| }); |
| |
| proxy.connectToAgent( |
| url, |
| serviceProviderProxy.ctrl.request(), |
| agentControllerProxy.ctrl.request(), |
| ); |
| |
| scheduleMicrotask(() { |
| if (!serviceCompleter.isCompleted) { |
| serviceCompleter.complete(serviceProviderProxy); |
| } |
| }); |
| |
| return serviceCompleter.future; |
| } |
| |
| /// See [fidl.ComponentContext#getPackageName]. |
| Future<String> getPackageName() { |
| Completer<String> completer = Completer<String>(); |
| try { |
| proxy.getPackageName(completer.complete); |
| } on Exception catch (err, stackTrace) { |
| completer.completeError(err, stackTrace); |
| } |
| return completer.future; |
| } |
| |
| void _handleConnectionError() { |
| log.warning('ComponentContextClient connection error'); |
| } |
| |
| void _handleBind() { |
| log.fine('proxy ready'); |
| _bind.complete(null); |
| } |
| |
| void _handleUnbind() { |
| log.fine('proxy unbound'); |
| } |
| |
| void _handleClose() { |
| log.fine('proxy closed'); |
| |
| for (fidl.AgentControllerProxy p in _agentControllers) { |
| p.ctrl.close(); |
| } |
| } |
| |
| /// Closes the underlying proxy connection, should be called as a response to |
| /// Lifecycle::terminate (see https://goo.gl/MmZ2dc). |
| Future<Null> terminate() async { |
| log.info('terminate called'); |
| proxy.ctrl.close(); |
| return; |
| } |
| } |