blob: 1fef677822ee56014fff3dd8429250d853052ea0 [file] [log] [blame]
// 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;
}
}