blob: 2699ab899a035c2f2514e5163471fdfbb68c9433 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.mojo.bindings;
import org.chromium.mojo.bindings.Callbacks.Callback1;
import org.chromium.mojo.bindings.Interface.AbstractProxy.HandlerImpl;
import org.chromium.mojo.bindings.interfacecontrol.QueryVersion;
import org.chromium.mojo.bindings.interfacecontrol.RequireVersion;
import org.chromium.mojo.bindings.interfacecontrol.RunInput;
import org.chromium.mojo.bindings.interfacecontrol.RunMessageParams;
import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeInput;
import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeMessageParams;
import org.chromium.mojo.bindings.interfacecontrol.RunOutput;
import org.chromium.mojo.bindings.interfacecontrol.RunResponseMessageParams;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.MessagePipeHandle;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojo.system.Pair;
import java.io.Closeable;
/**
* Base class for mojo generated interfaces.
*/
public interface Interface extends ConnectionErrorHandler, Closeable {
/**
* The close method is called when the connection to the interface is closed.
*
* @see java.io.Closeable#close()
*/
@Override
public void close();
/**
* A proxy to a mojo interface. This is base class for all generated proxies. It implements the
* Interface and each time a method is called, the parameters are serialized and sent to the
* {@link MessageReceiverWithResponder}, along with the response callback if needed.
*/
public interface Proxy extends Interface {
/**
* Class allowing to interact with the proxy itself.
*/
public interface Handler extends Closeable {
/**
* Sets the {@link ConnectionErrorHandler} that will be notified of errors.
*/
public void setErrorHandler(ConnectionErrorHandler errorHandler);
/**
* Unbinds the proxy and passes the handle. Can return null if the proxy is not bound or
* if the proxy is not over a message pipe.
*/
public MessagePipeHandle passHandle();
/**
* Returns the version number of the interface that the remote side supports.
*/
public int getVersion();
/**
* Queries the max version that the remote side supports. On completion, the result will
* be returned as the input of |callback|. The version number of this interface pointer
* will also be updated.
*/
public void queryVersion(Callback1<Integer> callback);
/**
* If the remote side doesn't support the specified version, it will close its end of
* the message pipe asynchronously. The call does nothing if |version| is no greater
* than getVersion().
* <p>
* If you make a call to requireVersion() with a version number X which is not supported
* by the remote side, it is guaranteed that all calls to the interface methods after
* requireVersion(X) will be ignored.
*/
public void requireVersion(int version);
}
/**
* Returns the {@link Handler} object allowing to interact with the proxy itself.
*/
public Handler getProxyHandler();
}
/**
* Base implementation of {@link Proxy}.
*/
abstract class AbstractProxy implements Proxy {
/**
* Implementation of {@link Handler}.
*/
protected static class HandlerImpl implements Proxy.Handler, ConnectionErrorHandler {
/**
* The {@link Core} implementation to use.
*/
private final Core mCore;
/**
* The {@link MessageReceiverWithResponder} that will receive a serialized message for
* each method call.
*/
private final MessageReceiverWithResponder mMessageReceiver;
/**
* The {@link ConnectionErrorHandler} that will be notified of errors.
*/
private ConnectionErrorHandler mErrorHandler;
/**
* The currently known version of the interface.
*/
private int mVersion;
/**
* Constructor.
*
* @param core the Core implementation used to create pipes and access the async waiter.
* @param messageReceiver the message receiver to send message to.
*/
protected HandlerImpl(Core core, MessageReceiverWithResponder messageReceiver) {
this.mCore = core;
this.mMessageReceiver = messageReceiver;
}
void setVersion(int version) {
mVersion = version;
}
/**
* Returns the message receiver to send message to.
*/
public MessageReceiverWithResponder getMessageReceiver() {
return mMessageReceiver;
}
/**
* Returns the Core implementation.
*/
public Core getCore() {
return mCore;
}
/**
* Sets the {@link ConnectionErrorHandler} that will be notified of errors.
*/
@Override
public void setErrorHandler(ConnectionErrorHandler errorHandler) {
this.mErrorHandler = errorHandler;
}
/**
* @see ConnectionErrorHandler#onConnectionError(MojoException)
*/
@Override
public void onConnectionError(MojoException e) {
if (mErrorHandler != null) {
mErrorHandler.onConnectionError(e);
}
}
/**
* @see Closeable#close()
*/
@Override
public void close() {
mMessageReceiver.close();
}
/**
* @see Interface.Proxy.Handler#passHandle()
*/
@Override
public MessagePipeHandle passHandle() {
@SuppressWarnings("unchecked")
HandleOwner<MessagePipeHandle> handleOwner =
(HandleOwner<MessagePipeHandle>) mMessageReceiver;
return handleOwner.passHandle();
}
/**
* @see Handler#getVersion()
*/
@Override
public int getVersion() {
return mVersion;
}
/**
* @see Handler#queryVersion(org.chromium.mojo.bindings.Callbacks.Callback1)
*/
@Override
public void queryVersion(final Callback1<Integer> callback) {
RunMessageParams message = new RunMessageParams();
message.input = new RunInput();
message.input.setQueryVersion(new QueryVersion());
InterfaceControlMessagesHelper.sendRunMessage(getCore(), mMessageReceiver, message,
new Callback1<RunResponseMessageParams>() {
@Override
public void call(RunResponseMessageParams response) {
if (response.output != null
&& response.output.which()
== RunOutput.Tag.QueryVersionResult) {
mVersion = response.output.getQueryVersionResult().version;
}
try {
callback.call(mVersion);
} catch (RuntimeException e) {
// TODO(lhchavez): Remove this hack. See b/28986534 for details.
android.util.Log.wtf("org.chromium.mojo.bindings.Interface",
"Uncaught runtime exception", e);
}
}
});
}
/**
* @see Handler#requireVersion(int)
*/
@Override
public void requireVersion(int version) {
if (mVersion >= version) {
return;
}
mVersion = version;
RunOrClosePipeMessageParams message = new RunOrClosePipeMessageParams();
message.input = new RunOrClosePipeInput();
message.input.setRequireVersion(new RequireVersion());
message.input.getRequireVersion().version = version;
InterfaceControlMessagesHelper.sendRunOrClosePipeMessage(
getCore(), mMessageReceiver, message);
}
}
/**
* The handler associated with this proxy.
*/
private final HandlerImpl mHandler;
protected AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) {
mHandler = new HandlerImpl(core, messageReceiver);
}
/**
* @see Interface#close()
*/
@Override
public void close() {
mHandler.close();
}
/**
* @see Proxy#getProxyHandler()
*/
@Override
public HandlerImpl getProxyHandler() {
return mHandler;
}
/**
* @see ConnectionErrorHandler#onConnectionError(org.chromium.mojo.system.MojoException)
*/
@Override
public void onConnectionError(MojoException e) {
mHandler.onConnectionError(e);
}
}
/**
* Base implementation of Stub. Stubs are message receivers that deserialize the payload and
* call the appropriate method in the implementation. If the method returns result, the stub
* serializes the response and sends it back.
*
* @param <I> the type of the interface to delegate calls to.
*/
abstract class Stub<I extends Interface> implements MessageReceiverWithResponder {
/**
* The {@link Core} implementation to use.
*/
private final Core mCore;
/**
* The implementation to delegate calls to.
*/
private final I mImpl;
/**
* Constructor.
*
* @param core the {@link Core} implementation to use.
* @param impl the implementation to delegate calls to.
*/
public Stub(Core core, I impl) {
mCore = core;
mImpl = impl;
}
/**
* Returns the Core implementation.
*/
protected Core getCore() {
return mCore;
}
/**
* Returns the implementation to delegate calls to.
*/
protected I getImpl() {
return mImpl;
}
/**
* @see org.chromium.mojo.bindings.MessageReceiver#close()
*/
@Override
public void close() {
mImpl.close();
}
}
/**
* The |Manager| object enables building of proxies and stubs for a given interface.
*
* @param <I> the type of the interface the manager can handle.
* @param <P> the type of the proxy the manager can handle. To be noted, P always extends I.
*/
abstract class Manager<I extends Interface, P extends Proxy> {
/**
* Returns the name of the interface. This is an opaque (but human readable) identifier used
* by the service provider to identify services.
*/
public abstract String getName();
/**
* Returns the version of the managed interface.
*/
public abstract int getVersion();
/**
* Binds the given implementation to the handle.
*/
public void bind(I impl, MessagePipeHandle handle) {
// The router (and by consequence the handle) is intentionally leaked. It will close
// itself when the connected handle is closed and the proxy receives the connection
// error.
Router router = new RouterImpl(handle);
bind(handle.getCore(), impl, router);
router.start();
}
/**
* Binds the given implementation to the InterfaceRequest.
*/
public final void bind(I impl, InterfaceRequest<I> request) {
bind(impl, request.passHandle());
}
/**
* Returns a Proxy that will send messages to the given |handle|. This implies that the
* other end of the handle must be bound to an implementation of the interface.
*/
public final P attachProxy(MessagePipeHandle handle, int version) {
RouterImpl router = new RouterImpl(handle);
P proxy = attachProxy(handle.getCore(), router);
DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler();
handlers.addConnectionErrorHandler(proxy);
router.setErrorHandler(handlers);
router.start();
((HandlerImpl) proxy.getProxyHandler()).setVersion(version);
return proxy;
}
/**
* Constructs a new |InterfaceRequest| for the interface. This method returns a Pair where
* the first element is a proxy, and the second element is the request. The proxy can be
* used immediately.
*/
public final Pair<P, InterfaceRequest<I>> getInterfaceRequest(Core core) {
Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
P proxy = attachProxy(handles.first, 0);
return Pair.create(proxy, new InterfaceRequest<I>(handles.second));
}
public final InterfaceRequest<I> asInterfaceRequest(MessagePipeHandle handle) {
return new InterfaceRequest<I>(handle);
}
/**
* Binds the implementation to the given |router|.
*/
final void bind(Core core, I impl, Router router) {
router.setErrorHandler(impl);
router.setIncomingMessageReceiver(buildStub(core, impl));
}
/**
* Returns a Proxy that will send messages to the given |router|.
*/
final P attachProxy(Core core, Router router) {
return buildProxy(core, new AutoCloseableRouter(core, router));
}
/**
* Creates a new array of the given |size|.
*/
protected abstract I[] buildArray(int size);
/**
* Constructs a Stub delegating to the given implementation.
*/
protected abstract Stub<I> buildStub(Core core, I impl);
/**
* Constructs a Proxy forwarding the calls to the given message receiver.
*/
protected abstract P buildProxy(Core core, MessageReceiverWithResponder messageReceiver);
}
}