// 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 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(). *

* 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 handleOwner = (HandleOwner) 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 callback) { RunMessageParams message = new RunMessageParams(); message.input = new RunInput(); message.input.setQueryVersion(new QueryVersion()); InterfaceControlMessagesHelper.sendRunMessage(getCore(), mMessageReceiver, message, new Callback1() { @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 the type of the interface to delegate calls to. */ abstract class Stub 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 the type of the interface the manager can handle. * @param

the type of the proxy the manager can handle. To be noted, P always extends I. */ abstract class Manager { /** * 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 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> getInterfaceRequest(Core core) { Pair handles = core.createMessagePipe(null); P proxy = attachProxy(handles.first, 0); return Pair.create(proxy, new InterfaceRequest(handles.second)); } public final InterfaceRequest asInterfaceRequest(MessagePipeHandle handle) { return new InterfaceRequest(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 buildStub(Core core, I impl); /** * Constructs a Proxy forwarding the calls to the given message receiver. */ protected abstract P buildProxy(Core core, MessageReceiverWithResponder messageReceiver); } }