summaryrefslogtreecommitdiff
path: root/mojo/public/java
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/public/java')
-rw-r--r--mojo/public/java/BUILD.gn64
-rw-r--r--mojo/public/java/bindings/README.md12
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java11
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java11
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java116
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java199
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java130
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java15
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java214
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java70
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java776
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java49
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java26
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java587
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java169
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java29
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java425
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java105
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java58
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java69
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java248
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java25
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java21
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java31
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java274
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java26
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java73
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java21
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java88
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java30
-rw-r--r--mojo/public/java/system/README.md25
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/Core.java183
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java334
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/Flags.java83
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/Handle.java61
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java219
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java224
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java44
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java82
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/Pair.java67
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java34
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java41
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java160
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java45
-rw-r--r--mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java38
45 files changed, 5612 insertions, 0 deletions
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
new file mode 100644
index 0000000000..078064114e
--- /dev/null
+++ b/mojo/public/java/BUILD.gn
@@ -0,0 +1,64 @@
+# 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.
+
+import("//build/config/android/rules.gni")
+
+android_library("system_java") {
+ java_files = [
+ "system/src/org/chromium/mojo/system/Core.java",
+ "system/src/org/chromium/mojo/system/DataPipe.java",
+ "system/src/org/chromium/mojo/system/Flags.java",
+ "system/src/org/chromium/mojo/system/Handle.java",
+ "system/src/org/chromium/mojo/system/InvalidHandle.java",
+ "system/src/org/chromium/mojo/system/MessagePipeHandle.java",
+ "system/src/org/chromium/mojo/system/MojoException.java",
+ "system/src/org/chromium/mojo/system/MojoResult.java",
+ "system/src/org/chromium/mojo/system/Pair.java",
+ "system/src/org/chromium/mojo/system/ResultAnd.java",
+ "system/src/org/chromium/mojo/system/SharedBufferHandle.java",
+ "system/src/org/chromium/mojo/system/UntypedHandle.java",
+ "system/src/org/chromium/mojo/system/RunLoop.java",
+ "system/src/org/chromium/mojo/system/Watcher.java",
+ ]
+}
+
+android_library("bindings_java") {
+ java_files = [
+ "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java",
+ "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java",
+ "bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java",
+ "bindings/src/org/chromium/mojo/bindings/BindingsHelper.java",
+ "bindings/src/org/chromium/mojo/bindings/Callbacks.java",
+ "bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java",
+ "bindings/src/org/chromium/mojo/bindings/Connector.java",
+ "bindings/src/org/chromium/mojo/bindings/DataHeader.java",
+ "bindings/src/org/chromium/mojo/bindings/Decoder.java",
+ "bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java",
+ "bindings/src/org/chromium/mojo/bindings/DeserializationException.java",
+ "bindings/src/org/chromium/mojo/bindings/Encoder.java",
+ "bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java",
+ "bindings/src/org/chromium/mojo/bindings/HandleOwner.java",
+ "bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java",
+ "bindings/src/org/chromium/mojo/bindings/Interface.java",
+ "bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java",
+ "bindings/src/org/chromium/mojo/bindings/MessageHeader.java",
+ "bindings/src/org/chromium/mojo/bindings/Message.java",
+ "bindings/src/org/chromium/mojo/bindings/MessageReceiver.java",
+ "bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java",
+ "bindings/src/org/chromium/mojo/bindings/RouterImpl.java",
+ "bindings/src/org/chromium/mojo/bindings/Router.java",
+ "bindings/src/org/chromium/mojo/bindings/SerializationException.java",
+ "bindings/src/org/chromium/mojo/bindings/ServiceMessage.java",
+ "bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java",
+ "bindings/src/org/chromium/mojo/bindings/Struct.java",
+ "bindings/src/org/chromium/mojo/bindings/Union.java",
+ ]
+
+ deps = [
+ ":system_java",
+ "//base:base_java",
+ ]
+
+ srcjar_deps = [ "../interfaces/bindings:bindings_java_sources" ]
+}
diff --git a/mojo/public/java/bindings/README.md b/mojo/public/java/bindings/README.md
new file mode 100644
index 0000000000..821a230ed9
--- /dev/null
+++ b/mojo/public/java/bindings/README.md
@@ -0,0 +1,12 @@
+# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo Java Bindings API
+This document is a subset of the [Mojo documentation](/mojo).
+
+[TOC]
+
+## Overview
+
+This document provides a brief guide to API usage with example code snippets.
+For a detailed API references please consult the class definitions in
+[this directory](https://cs.chromium.org/chromium/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/)
+
+TODO: Make the contents of this document less non-existent.
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java
new file mode 100644
index 0000000000..ee8f6310a1
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java
@@ -0,0 +1,11 @@
+// Copyright 2015 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;
+
+/**
+ * Associated interface is not supported yet.
+ */
+public class AssociatedInterfaceNotSupported {
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java
new file mode 100644
index 0000000000..1b07cd11ba
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java
@@ -0,0 +1,11 @@
+// Copyright 2015 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;
+
+/**
+ * Associated interface is not supported yet.
+ */
+public class AssociatedInterfaceRequestNotSupported {
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
new file mode 100644
index 0000000000..8a83be928e
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
@@ -0,0 +1,116 @@
+// 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.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Wrapper around {@link Router} that will close the connection when not referenced anymore.
+ */
+class AutoCloseableRouter implements Router {
+
+ /**
+ * The underlying router.
+ */
+ private final Router mRouter;
+
+ /**
+ * The executor to close the underlying router.
+ */
+ private final Executor mExecutor;
+
+ /**
+ * Flags to keep track if this router has been correctly closed.
+ */
+ private boolean mClosed;
+
+ /**
+ * Constructor.
+ */
+ public AutoCloseableRouter(Core core, Router router) {
+ mRouter = router;
+ mExecutor = ExecutorFactory.getExecutorForCurrentThread(core);
+ }
+
+ /**
+ * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
+ */
+ @Override
+ public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
+ mRouter.setIncomingMessageReceiver(incomingMessageReceiver);
+ }
+
+ /**
+ * @see HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ return mRouter.passHandle();
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ return mRouter.accept(message);
+ }
+
+ /**
+ * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+ */
+ @Override
+ public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+ return mRouter.acceptWithResponder(message, responder);
+
+ }
+
+ /**
+ * @see Router#start()
+ */
+ @Override
+ public void start() {
+ mRouter.start();
+ }
+
+ /**
+ * @see Router#setErrorHandler(ConnectionErrorHandler)
+ */
+ @Override
+ public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+ mRouter.setErrorHandler(errorHandler);
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ mRouter.close();
+ mClosed = true;
+ }
+
+ /**
+ * @see Object#finalize()
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ if (!mClosed) {
+ mExecutor.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ close();
+ }
+ });
+ throw new IllegalStateException("Warning: Router objects should be explicitly closed " +
+ "when no longer required otherwise you may leak handles.");
+ }
+ super.finalize();
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
new file mode 100644
index 0000000000..f77399d1b4
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
@@ -0,0 +1,199 @@
+// 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.system.Handle;
+import org.chromium.mojo.system.Watcher;
+
+/**
+ * Helper functions.
+ */
+public class BindingsHelper {
+ /**
+ * Alignment in bytes for mojo serialization.
+ */
+ public static final int ALIGNMENT = 8;
+
+ /**
+ * The size, in bytes, of a serialized handle. A handle is serialized as an int representing the
+ * offset of the handle in the list of handles.
+ */
+ public static final int SERIALIZED_HANDLE_SIZE = 4;
+
+ /**
+ * The size, in bytes, of a serialized interface, which consists of a serialized handle (4
+ * bytes) and a version number (4 bytes).
+ */
+ public static final int SERIALIZED_INTERFACE_SIZE = 8;
+
+ /**
+ * The size, in bytes, of a serialized pointer. A pointer is serializaed as an unsigned long
+ * representing the offset from its position to the pointed elemnt.
+ */
+ public static final int POINTER_SIZE = 8;
+
+ /**
+ * The size, in bytes, of a serialized union.
+ */
+ public static final int UNION_SIZE = 16;
+
+ /**
+ * The header for a serialized map element.
+ */
+ public static final DataHeader MAP_STRUCT_HEADER = new DataHeader(24, 0);
+
+ /**
+ * The value used for the expected length of a non-fixed size array.
+ */
+ public static final int UNSPECIFIED_ARRAY_LENGTH = -1;
+
+ /**
+ * Passed as |arrayNullability| when neither the array nor its elements are nullable.
+ */
+ public static final int NOTHING_NULLABLE = 0;
+
+ /**
+ * "Array bit" of |arrayNullability| is set iff the array itself is nullable.
+ */
+ public static final int ARRAY_NULLABLE = (1 << 0);
+
+ /**
+ * "Element bit" of |arrayNullability| is set iff the array elements are nullable.
+ */
+ public static final int ELEMENT_NULLABLE = (1 << 1);
+
+ public static boolean isArrayNullable(int arrayNullability) {
+ return (arrayNullability & ARRAY_NULLABLE) > 0;
+ }
+
+ public static boolean isElementNullable(int arrayNullability) {
+ return (arrayNullability & ELEMENT_NULLABLE) > 0;
+ }
+
+ /**
+ * Align |size| on {@link BindingsHelper#ALIGNMENT}.
+ */
+ public static int align(int size) {
+ return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+ }
+
+ /**
+ * Align |size| on {@link BindingsHelper#ALIGNMENT}.
+ */
+ public static long align(long size) {
+ return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+ }
+
+ /**
+ * Compute the size in bytes of the given string encoded as utf8.
+ */
+ public static int utf8StringSizeInBytes(String s) {
+ int res = 0;
+ for (int i = 0; i < s.length(); ++i) {
+ char c = s.charAt(i);
+ int codepoint = c;
+ if (isSurrogate(c)) {
+ i++;
+ char c2 = s.charAt(i);
+ codepoint = Character.toCodePoint(c, c2);
+ }
+ res += 1;
+ if (codepoint > 0x7f) {
+ res += 1;
+ if (codepoint > 0x7ff) {
+ res += 1;
+ if (codepoint > 0xffff) {
+ res += 1;
+ if (codepoint > 0x1fffff) {
+ res += 1;
+ if (codepoint > 0x3ffffff) {
+ res += 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Returns |true| if and only if the two objects are equals, handling |null|.
+ */
+ public static boolean equals(Object o1, Object o2) {
+ if (o1 == o2) {
+ return true;
+ }
+ if (o1 == null) {
+ return false;
+ }
+ return o1.equals(o2);
+ }
+
+ /**
+ * Returns the hash code of the object, handling |null|.
+ */
+ public static int hashCode(Object o) {
+ if (o == null) {
+ return 0;
+ }
+ return o.hashCode();
+ }
+
+ /**
+ * Returns the hash code of the value.
+ */
+ public static int hashCode(boolean o) {
+ return o ? 1231 : 1237;
+ }
+
+ /**
+ * Returns the hash code of the value.
+ */
+ public static int hashCode(long o) {
+ return (int) (o ^ (o >>> 32));
+ }
+
+ /**
+ * Returns the hash code of the value.
+ */
+ public static int hashCode(float o) {
+ return Float.floatToIntBits(o);
+ }
+
+ /**
+ * Returns the hash code of the value.
+ */
+ public static int hashCode(double o) {
+ return hashCode(Double.doubleToLongBits(o));
+ }
+
+ /**
+ * Returns the hash code of the value.
+ */
+ public static int hashCode(int o) {
+ return o;
+ }
+
+ /**
+ * Determines if the given {@code char} value is a Unicode <i>surrogate code unit</i>. See
+ * {@link Character#isSurrogate}. Extracting here because the method only exists at API level
+ * 19.
+ */
+ private static boolean isSurrogate(char c) {
+ return c >= Character.MIN_SURROGATE && c < (Character.MAX_SURROGATE + 1);
+ }
+
+ /**
+ * Returns an {@link AsyncWaiter} to use with the given handle, or |null| if none if available.
+ */
+ static Watcher getWatcherForHandle(Handle handle) {
+ if (handle.getCore() != null) {
+ return handle.getCore().getWatcher();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java
new file mode 100644
index 0000000000..c6b14c1494
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java
@@ -0,0 +1,130 @@
+// 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.
+
+// This file was generated using
+// mojo/tools/generate_java_callback_interfaces.py
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Contains a generic interface for callbacks.
+ */
+public interface Callbacks {
+
+ /**
+ * A generic callback.
+ */
+ interface Callback0 {
+ /**
+ * Call the callback.
+ */
+ public void call();
+ }
+
+ /**
+ * A generic 1-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ */
+ interface Callback1<T1> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1);
+ }
+
+ /**
+ * A generic 2-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ */
+ interface Callback2<T1, T2> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2);
+ }
+
+ /**
+ * A generic 3-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ */
+ interface Callback3<T1, T2, T3> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3);
+ }
+
+ /**
+ * A generic 4-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ */
+ interface Callback4<T1, T2, T3, T4> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+ }
+
+ /**
+ * A generic 5-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ * @param <T5> the type of argument 5.
+ */
+ interface Callback5<T1, T2, T3, T4, T5> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
+ }
+
+ /**
+ * A generic 6-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ * @param <T5> the type of argument 5.
+ * @param <T6> the type of argument 6.
+ */
+ interface Callback6<T1, T2, T3, T4, T5, T6> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
+ }
+
+ /**
+ * A generic 7-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ * @param <T5> the type of argument 5.
+ * @param <T6> the type of argument 6.
+ * @param <T7> the type of argument 7.
+ */
+ interface Callback7<T1, T2, T3, T4, T5, T6, T7> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
new file mode 100644
index 0000000000..f601fb81b5
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
@@ -0,0 +1,15 @@
+// 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.system.MojoException;
+
+/**
+ * A {@link ConnectionErrorHandler} is notified of an error happening while using the bindings over
+ * message pipes.
+ */
+public interface ConnectionErrorHandler {
+ public void onConnectionError(MojoException e);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
new file mode 100644
index 0000000000..2aa5ea690c
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -0,0 +1,214 @@
+// 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.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.ResultAnd;
+import org.chromium.mojo.system.Watcher;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A {@link Connector} owns a {@link MessagePipeHandle} and will send any received messages to the
+ * registered {@link MessageReceiver}. It also acts as a {@link MessageReceiver} and will send any
+ * message through the handle.
+ * <p>
+ * The method |start| must be called before the {@link Connector} will start listening to incoming
+ * messages.
+ */
+public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle> {
+
+ /**
+ * The callback that is notified when the state of the owned handle changes.
+ */
+ private final WatcherCallback mWatcherCallback = new WatcherCallback();
+
+ /**
+ * The owned message pipe.
+ */
+ private final MessagePipeHandle mMessagePipeHandle;
+
+ /**
+ * A watcher which is notified when a new message is available on the owned message pipe.
+ */
+ private final Watcher mWatcher;
+
+ /**
+ * The {@link MessageReceiver} to which received messages are sent.
+ */
+ private MessageReceiver mIncomingMessageReceiver;
+
+ /**
+ * The error handler to notify of errors.
+ */
+ private ConnectionErrorHandler mErrorHandler;
+
+ /**
+ * Create a new connector over a |messagePipeHandle|. The created connector will use the default
+ * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|.
+ */
+ public Connector(MessagePipeHandle messagePipeHandle) {
+ this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle));
+ }
+
+ /**
+ * Create a new connector over a |messagePipeHandle| using the given {@link AsyncWaiter} to get
+ * notified of changes on the handle.
+ */
+ public Connector(MessagePipeHandle messagePipeHandle, Watcher watcher) {
+ mMessagePipeHandle = messagePipeHandle;
+ mWatcher = watcher;
+ }
+
+ /**
+ * Set the {@link MessageReceiver} that will receive message from the owned message pipe.
+ */
+ public void setIncomingMessageReceiver(MessageReceiver incomingMessageReceiver) {
+ mIncomingMessageReceiver = incomingMessageReceiver;
+ }
+
+ /**
+ * Set the {@link ConnectionErrorHandler} that will be notified of errors on the owned message
+ * pipe.
+ */
+ public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+ mErrorHandler = errorHandler;
+ }
+
+ /**
+ * Start listening for incoming messages.
+ */
+ public void start() {
+ mWatcher.start(mMessagePipeHandle, Core.HandleSignals.READABLE, mWatcherCallback);
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ try {
+ mMessagePipeHandle.writeMessage(message.getData(),
+ message.getHandles(), MessagePipeHandle.WriteFlags.NONE);
+ return true;
+ } catch (MojoException e) {
+ onError(e);
+ return false;
+ }
+ }
+
+ /**
+ * Pass the owned handle of the connector. After this, the connector is disconnected. It cannot
+ * accept new message and it isn't listening to the handle anymore.
+ *
+ * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ cancelIfActive();
+ MessagePipeHandle handle = mMessagePipeHandle.pass();
+ if (mIncomingMessageReceiver != null) {
+ mIncomingMessageReceiver.close();
+ }
+ return handle;
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ cancelIfActive();
+ mMessagePipeHandle.close();
+ if (mIncomingMessageReceiver != null) {
+ MessageReceiver incomingMessageReceiver = mIncomingMessageReceiver;
+ mIncomingMessageReceiver = null;
+ incomingMessageReceiver.close();
+ }
+ }
+
+ private class WatcherCallback implements Watcher.Callback {
+ /**
+ * @see org.chromium.mojo.system.Watcher.Callback#onResult(int)
+ */
+ @Override
+ public void onResult(int result) {
+ Connector.this.onWatcherResult(result);
+ }
+
+ }
+
+ /**
+ * @see org.chromium.mojo.system.Watcher.Callback#onResult(int)
+ */
+ private void onWatcherResult(int result) {
+ if (result == MojoResult.OK) {
+ readOutstandingMessages();
+ } else {
+ onError(new MojoException(result));
+ }
+ }
+
+ private void onError(MojoException exception) {
+ close();
+ if (mErrorHandler != null) {
+ mErrorHandler.onConnectionError(exception);
+ }
+ }
+
+ /**
+ * Read all available messages on the owned message pipe.
+ */
+ private void readOutstandingMessages() {
+ ResultAnd<Boolean> result;
+ do {
+ try {
+ result = readAndDispatchMessage(mMessagePipeHandle, mIncomingMessageReceiver);
+ } catch (MojoException e) {
+ onError(e);
+ return;
+ }
+ } while (result.getValue());
+ if (result.getMojoResult() != MojoResult.SHOULD_WAIT) {
+ onError(new MojoException(result.getMojoResult()));
+ }
+ }
+
+ private void cancelIfActive() {
+ mWatcher.cancel();
+ mWatcher.destroy();
+ }
+
+ /**
+ * Read a message, and pass it to the given |MessageReceiver| if not null. If the
+ * |MessageReceiver| is null, the message is lost.
+ *
+ * @param receiver The {@link MessageReceiver} that will receive the read {@link Message}. Can
+ * be <code>null</code>, in which case the message is discarded.
+ */
+ static ResultAnd<Boolean> readAndDispatchMessage(
+ MessagePipeHandle handle, MessageReceiver receiver) {
+ // TODO(qsr) Allow usage of a pool of pre-allocated buffer for performance.
+ ResultAnd<ReadMessageResult> result =
+ handle.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE);
+ if (result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) {
+ return new ResultAnd<Boolean>(result.getMojoResult(), false);
+ }
+ ReadMessageResult readResult = result.getValue();
+ assert readResult != null;
+ ByteBuffer buffer = ByteBuffer.allocateDirect(readResult.getMessageSize());
+ result = handle.readMessage(
+ buffer, readResult.getHandlesCount(), MessagePipeHandle.ReadFlags.NONE);
+ if (receiver != null && result.getMojoResult() == MojoResult.OK) {
+ boolean accepted = receiver.accept(new Message(buffer, result.getValue().getHandles()));
+ return new ResultAnd<Boolean>(result.getMojoResult(), accepted);
+ }
+ return new ResultAnd<Boolean>(result.getMojoResult(), false);
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java
new file mode 100644
index 0000000000..96acec9438
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java
@@ -0,0 +1,70 @@
+// Copyright 2015 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;
+
+/**
+ * The header for a mojo complex element.
+ */
+public final class DataHeader {
+ /**
+ * The size of a serialized header, in bytes.
+ */
+ public static final int HEADER_SIZE = 8;
+
+ /**
+ * The offset of the size field.
+ */
+ public static final int SIZE_OFFSET = 0;
+
+ /**
+ * The offset of the number of fields field.
+ */
+ public static final int ELEMENTS_OR_VERSION_OFFSET = 4;
+
+ /**
+ * The size of the object owning this header.
+ */
+ public final int size;
+
+ /**
+ * Number of element (for an array) or version (for a struct) of the object owning this
+ * header.
+ */
+ public final int elementsOrVersion;
+
+ /**
+ * Constructor.
+ */
+ public DataHeader(int size, int elementsOrVersion) {
+ super();
+ this.size = size;
+ this.elementsOrVersion = elementsOrVersion;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + elementsOrVersion;
+ result = prime * result + size;
+ return result;
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) return true;
+ if (object == null) return false;
+ if (getClass() != object.getClass()) return false;
+
+ DataHeader other = (DataHeader) object;
+ return (elementsOrVersion == other.elementsOrVersion && size == other.size);
+ }
+} \ No newline at end of file
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
new file mode 100644
index 0000000000..64ff1c08a1
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
@@ -0,0 +1,776 @@
+// 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.Interface.Proxy;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.InvalidHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.UntypedHandle;
+
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+
+/**
+ * A Decoder is a helper class for deserializing a mojo struct. It enables deserialization of basic
+ * types from a {@link Message} object at a given offset into it's byte buffer.
+ */
+public class Decoder {
+
+ /**
+ * Helper class to validate the decoded message.
+ */
+ static final class Validator {
+
+ /**
+ * Minimal value for the next handle to deserialize.
+ */
+ private int mMinNextClaimedHandle;
+ /**
+ * Minimal value of the start of the next memory to claim.
+ */
+ private long mMinNextMemory;
+ /**
+ * The current nesting level when decoding.
+ */
+ private long mStackDepth;
+
+ /**
+ * The maximal memory accessible.
+ */
+ private final long mMaxMemory;
+
+ /**
+ * The number of handles in the message.
+ */
+ private final long mNumberOfHandles;
+
+ /**
+ * The maximum nesting level when decoding.
+ */
+ private static final int MAX_RECURSION_DEPTH = 100;
+
+ /**
+ * Constructor.
+ */
+ Validator(long maxMemory, int numberOfHandles) {
+ mMaxMemory = maxMemory;
+ mNumberOfHandles = numberOfHandles;
+ mStackDepth = 0;
+ }
+
+ public void claimHandle(int handle) {
+ if (handle < mMinNextClaimedHandle) {
+ throw new DeserializationException(
+ "Trying to access handle out of order.");
+ }
+ if (handle >= mNumberOfHandles) {
+ throw new DeserializationException("Trying to access non present handle.");
+ }
+ mMinNextClaimedHandle = handle + 1;
+ }
+
+ public void claimMemory(long start, long end) {
+ if (start % BindingsHelper.ALIGNMENT != 0) {
+ throw new DeserializationException("Incorrect starting alignment: " + start + ".");
+ }
+ if (start < mMinNextMemory) {
+ throw new DeserializationException("Trying to access memory out of order.");
+ }
+ if (end < start) {
+ throw new DeserializationException("Incorrect memory range.");
+ }
+ if (end > mMaxMemory) {
+ throw new DeserializationException("Trying to access out of range memory.");
+ }
+ mMinNextMemory = BindingsHelper.align(end);
+ }
+
+ public void increaseStackDepth() {
+ ++mStackDepth;
+ if (mStackDepth >= MAX_RECURSION_DEPTH) {
+ throw new DeserializationException("Recursion depth limit exceeded.");
+ }
+ }
+
+ public void decreaseStackDepth() {
+ --mStackDepth;
+ }
+ }
+
+ /**
+ * The message to deserialize from.
+ */
+ private final Message mMessage;
+
+ /**
+ * The base offset in the byte buffer.
+ */
+ private final int mBaseOffset;
+
+ /**
+ * Validator for the decoded message.
+ */
+ private final Validator mValidator;
+
+ /**
+ * Constructor.
+ *
+ * @param message The message to decode.
+ */
+ public Decoder(Message message) {
+ this(message, new Validator(message.getData().limit(), message.getHandles().size()), 0);
+ }
+
+ private Decoder(Message message, Validator validator, int baseOffset) {
+ mMessage = message;
+ mMessage.getData().order(ByteOrder.LITTLE_ENDIAN);
+ mBaseOffset = baseOffset;
+ mValidator = validator;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the current position.
+ */
+ public DataHeader readDataHeader() {
+ // Claim the memory for the header.
+ mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE);
+ DataHeader result = readDataHeaderAtOffset(0, false);
+ // Claim the rest of the memory.
+ mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + result.size);
+ return result;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} for an union at the given offset.
+ */
+ public DataHeader readDataHeaderForUnion(int offset) {
+ DataHeader result = readDataHeaderAtOffset(offset, true);
+ if (result.size == 0) {
+ if (result.elementsOrVersion != 0) {
+ throw new DeserializationException(
+ "Unexpected version tag for a null union. Expecting 0, found: "
+ + result.elementsOrVersion);
+ }
+ } else if (result.size != BindingsHelper.UNION_SIZE) {
+ throw new DeserializationException(
+ "Unexpected size of an union. The size must be 0 for a null union, or 16 for "
+ + "a non-null union.");
+ }
+ return result;
+ }
+
+ /**
+ * @returns a decoder suitable to decode an union defined as the root object of a message.
+ */
+ public Decoder decoderForSerializedUnion() {
+ mValidator.claimMemory(0, BindingsHelper.UNION_SIZE);
+ return this;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset.
+ */
+ private DataHeader readDataHeaderAtOffset(int offset, boolean isUnion) {
+ int size = readInt(offset + DataHeader.SIZE_OFFSET);
+ int elementsOrVersion = readInt(offset + DataHeader.ELEMENTS_OR_VERSION_OFFSET);
+ if (size < 0) {
+ throw new DeserializationException(
+ "Negative size. Unsigned integers are not valid for java.");
+ }
+ if (elementsOrVersion < 0 && (!isUnion || elementsOrVersion != -1)) {
+ throw new DeserializationException(
+ "Negative elements or version. Unsigned integers are not valid for java.");
+ }
+
+ return new DataHeader(size, elementsOrVersion);
+ }
+
+ public DataHeader readAndValidateDataHeader(DataHeader[] versionArray) {
+ DataHeader header = readDataHeader();
+ int maxVersionIndex = versionArray.length - 1;
+ if (header.elementsOrVersion <= versionArray[maxVersionIndex].elementsOrVersion) {
+ DataHeader referenceHeader = null;
+ for (int index = maxVersionIndex; index >= 0; index--) {
+ DataHeader dataHeader = versionArray[index];
+ if (header.elementsOrVersion >= dataHeader.elementsOrVersion) {
+ referenceHeader = dataHeader;
+ break;
+ }
+ }
+ if (referenceHeader == null || referenceHeader.size != header.size) {
+ throw new DeserializationException(
+ "Header doesn't correspond to any known version.");
+ }
+ } else {
+ if (header.size < versionArray[maxVersionIndex].size) {
+ throw new DeserializationException("Message newer than the last known version"
+ + " cannot be shorter than required by the last known version.");
+ }
+ }
+ return header;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+ * array where elements are pointers.
+ */
+ public DataHeader readDataHeaderForPointerArray(int expectedLength) {
+ return readDataHeaderForArray(BindingsHelper.POINTER_SIZE, expectedLength);
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+ * array where elements are unions.
+ */
+ public DataHeader readDataHeaderForUnionArray(int expectedLength) {
+ return readDataHeaderForArray(BindingsHelper.UNION_SIZE, expectedLength);
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for a map.
+ */
+ public void readDataHeaderForMap() {
+ DataHeader si = readDataHeader();
+ if (si.size != BindingsHelper.MAP_STRUCT_HEADER.size) {
+ throw new DeserializationException(
+ "Incorrect header for map. The size is incorrect.");
+ }
+ if (si.elementsOrVersion != BindingsHelper.MAP_STRUCT_HEADER.elementsOrVersion) {
+ throw new DeserializationException(
+ "Incorrect header for map. The version is incorrect.");
+ }
+ }
+
+ /**
+ * Deserializes a byte at the given offset.
+ */
+ public byte readByte(int offset) {
+ validateBufferSize(offset, 1);
+ return mMessage.getData().get(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a boolean at the given offset, re-using any partially read byte.
+ */
+ public boolean readBoolean(int offset, int bit) {
+ validateBufferSize(offset, 1);
+ return (readByte(offset) & (1 << bit)) != 0;
+ }
+
+ /**
+ * Deserializes a short at the given offset.
+ */
+ public short readShort(int offset) {
+ validateBufferSize(offset, 2);
+ return mMessage.getData().getShort(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes an int at the given offset.
+ */
+ public int readInt(int offset) {
+ validateBufferSize(offset, 4);
+ return mMessage.getData().getInt(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a float at the given offset.
+ */
+ public float readFloat(int offset) {
+ validateBufferSize(offset, 4);
+ return mMessage.getData().getFloat(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a long at the given offset.
+ */
+ public long readLong(int offset) {
+ validateBufferSize(offset, 8);
+ return mMessage.getData().getLong(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a double at the given offset.
+ */
+ public double readDouble(int offset) {
+ validateBufferSize(offset, 8);
+ return mMessage.getData().getDouble(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content
+ * of the pointer.
+ */
+ public Decoder readPointer(int offset, boolean nullable) {
+ int basePosition = mBaseOffset + offset;
+ long pointerOffset = readLong(offset);
+ if (pointerOffset == 0) {
+ if (!nullable) {
+ throw new DeserializationException(
+ "Trying to decode null pointer for a non-nullable type.");
+ }
+ return null;
+ }
+ int newPosition = (int) (basePosition + pointerOffset);
+ // The method |getDecoderAtPosition| will validate that the pointer address is valid.
+ return getDecoderAtPosition(newPosition);
+
+ }
+
+ /**
+ * Deserializes an array of boolean at the given offset.
+ */
+ public boolean[] readBooleans(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForBooleanArray(expectedLength);
+ byte[] bytes = new byte[(si.elementsOrVersion + 7) / BindingsHelper.ALIGNMENT];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().get(bytes);
+ boolean[] result = new boolean[si.elementsOrVersion];
+ for (int i = 0; i < bytes.length; ++i) {
+ for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
+ int booleanIndex = i * BindingsHelper.ALIGNMENT + j;
+ if (booleanIndex < result.length) {
+ result[booleanIndex] = (bytes[i] & (1 << j)) != 0;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of bytes at the given offset.
+ */
+ public byte[] readBytes(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(1, expectedLength);
+ byte[] result = new byte[si.elementsOrVersion];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of shorts at the given offset.
+ */
+ public short[] readShorts(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(2, expectedLength);
+ short[] result = new short[si.elementsOrVersion];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asShortBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of ints at the given offset.
+ */
+ public int[] readInts(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ int[] result = new int[si.elementsOrVersion];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asIntBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of floats at the given offset.
+ */
+ public float[] readFloats(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ float[] result = new float[si.elementsOrVersion];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asFloatBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of longs at the given offset.
+ */
+ public long[] readLongs(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(8, expectedLength);
+ long[] result = new long[si.elementsOrVersion];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asLongBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of doubles at the given offset.
+ */
+ public double[] readDoubles(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(8, expectedLength);
+ double[] result = new double[si.elementsOrVersion];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asDoubleBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an |Handle| at the given offset.
+ */
+ public Handle readHandle(int offset, boolean nullable) {
+ int index = readInt(offset);
+ if (index == -1) {
+ if (!nullable) {
+ throw new DeserializationException(
+ "Trying to decode an invalid handle for a non-nullable type.");
+ }
+ return InvalidHandle.INSTANCE;
+ }
+ mValidator.claimHandle(index);
+ return mMessage.getHandles().get(index);
+ }
+
+ /**
+ * Deserializes an |UntypedHandle| at the given offset.
+ */
+ public UntypedHandle readUntypedHandle(int offset, boolean nullable) {
+ return readHandle(offset, nullable).toUntypedHandle();
+ }
+
+ /**
+ * Deserializes a |ConsumerHandle| at the given offset.
+ */
+ public DataPipe.ConsumerHandle readConsumerHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toDataPipeConsumerHandle();
+ }
+
+ /**
+ * Deserializes a |ProducerHandle| at the given offset.
+ */
+ public DataPipe.ProducerHandle readProducerHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toDataPipeProducerHandle();
+ }
+
+ /**
+ * Deserializes a |MessagePipeHandle| at the given offset.
+ */
+ public MessagePipeHandle readMessagePipeHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toMessagePipeHandle();
+ }
+
+ /**
+ * Deserializes a |SharedBufferHandle| at the given offset.
+ */
+ public SharedBufferHandle readSharedBufferHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toSharedBufferHandle();
+ }
+
+ /**
+ * Deserializes an interface at the given offset.
+ *
+ * @return a proxy to the service.
+ */
+ public <P extends Proxy> P readServiceInterface(int offset, boolean nullable,
+ Interface.Manager<?, P> manager) {
+ MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+ if (!handle.isValid()) {
+ return null;
+ }
+ int version = readInt(offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+ return manager.attachProxy(handle, version);
+ }
+
+ /**
+ * Deserializes a |InterfaceRequest| at the given offset.
+ */
+ public <I extends Interface> InterfaceRequest<I> readInterfaceRequest(int offset,
+ boolean nullable) {
+ MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+ if (handle == null) {
+ return null;
+ }
+ return new InterfaceRequest<I>(handle);
+ }
+
+ /**
+ * Deserializes an associated interface at the given offset. Not yet supported.
+ */
+ public AssociatedInterfaceNotSupported readAssociatedServiceInterfaceNotSupported(int offset,
+ boolean nullable) {
+ return null;
+ }
+
+ /**
+ * Deserializes an associated interface request at the given offset. Not yet supported.
+ */
+ public AssociatedInterfaceRequestNotSupported readAssociatedInterfaceRequestNotSupported(
+ int offset, boolean nullable) {
+ return null;
+ }
+
+ /**
+ * Deserializes a string at the given offset.
+ */
+ public String readString(int offset, boolean nullable) {
+ final int arrayNullability = nullable ? BindingsHelper.ARRAY_NULLABLE : 0;
+ byte[] bytes = readBytes(offset, arrayNullability, BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
+ if (bytes == null) {
+ return null;
+ }
+ return new String(bytes, Charset.forName("utf8"));
+ }
+
+ /**
+ * Deserializes an array of |Handle| at the given offset.
+ */
+ public Handle[] readHandles(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ Handle[] result = new Handle[si.elementsOrVersion];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |UntypedHandle| at the given offset.
+ */
+ public UntypedHandle[] readUntypedHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ UntypedHandle[] result = new UntypedHandle[si.elementsOrVersion];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readUntypedHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |ConsumerHandle| at the given offset.
+ */
+ public DataPipe.ConsumerHandle[] readConsumerHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.elementsOrVersion];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readConsumerHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |ProducerHandle| at the given offset.
+ */
+ public DataPipe.ProducerHandle[] readProducerHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.elementsOrVersion];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readProducerHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+
+ }
+
+ /**
+ * Deserializes an array of |MessagePipeHandle| at the given offset.
+ */
+ public MessagePipeHandle[] readMessagePipeHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ MessagePipeHandle[] result = new MessagePipeHandle[si.elementsOrVersion];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readMessagePipeHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+
+ }
+
+ /**
+ * Deserializes an array of |SharedBufferHandle| at the given offset.
+ */
+ public SharedBufferHandle[] readSharedBufferHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ SharedBufferHandle[] result = new SharedBufferHandle[si.elementsOrVersion];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readSharedBufferHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+
+ }
+
+ /**
+ * Deserializes an array of |ServiceHandle| at the given offset.
+ */
+ public <S extends Interface, P extends Proxy> S[] readServiceInterfaces(
+ int offset, int arrayNullability, int expectedLength, Interface.Manager<S, P> manager) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si =
+ d.readDataHeaderForArray(BindingsHelper.SERIALIZED_INTERFACE_SIZE, expectedLength);
+ S[] result = manager.buildArray(si.elementsOrVersion);
+ for (int i = 0; i < result.length; ++i) {
+ // This cast is necessary because java 6 doesn't handle wildcard correctly when using
+ // Manager<S, ? extends S>
+ @SuppressWarnings("unchecked")
+ S value = (S) d.readServiceInterface(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_INTERFACE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability), manager);
+ result[i] = value;
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |InterfaceRequest| at the given offset.
+ */
+ public <I extends Interface> InterfaceRequest<I>[] readInterfaceRequests(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ @SuppressWarnings("unchecked")
+ InterfaceRequest<I>[] result = new InterfaceRequest[si.elementsOrVersion];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readInterfaceRequest(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of associated interfaces at the given offset. Not yet supported.
+ */
+ public AssociatedInterfaceNotSupported[] readAssociatedServiceInterfaceNotSupporteds(
+ int offset, int arrayNullability, int expectedLength) {
+ return null;
+ }
+
+ /**
+ * Deserializes an array of associated interface requests at the given offset. Not yet
+ * supported.
+ */
+ public AssociatedInterfaceRequestNotSupported[] readAssociatedInterfaceRequestNotSupporteds(
+ int offset, int arrayNullability, int expectedLength) {
+ return null;
+ }
+
+ /**
+ * Returns a view of this decoder at the offset |offset|.
+ */
+ private Decoder getDecoderAtPosition(int offset) {
+ return new Decoder(mMessage, mValidator, offset);
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+ * array of booleans.
+ */
+ private DataHeader readDataHeaderForBooleanArray(int expectedLength) {
+ DataHeader dataHeader = readDataHeader();
+ if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.elementsOrVersion + 7) / 8) {
+ throw new DeserializationException("Array header is incorrect.");
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+ && dataHeader.elementsOrVersion != expectedLength) {
+ throw new DeserializationException("Incorrect array length. Expected: " + expectedLength
+ + ", but got: " + dataHeader.elementsOrVersion + ".");
+ }
+ return dataHeader;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} of an array at the given offset.
+ */
+ private DataHeader readDataHeaderForArray(long elementSize, int expectedLength) {
+ DataHeader dataHeader = readDataHeader();
+ if (dataHeader.size
+ < (DataHeader.HEADER_SIZE + elementSize * dataHeader.elementsOrVersion)) {
+ throw new DeserializationException("Array header is incorrect.");
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+ && dataHeader.elementsOrVersion != expectedLength) {
+ throw new DeserializationException("Incorrect array length. Expected: " + expectedLength
+ + ", but got: " + dataHeader.elementsOrVersion + ".");
+ }
+ return dataHeader;
+ }
+
+ private void validateBufferSize(int offset, int size) {
+ if (mMessage.getData().limit() < offset + size) {
+ throw new DeserializationException("Buffer is smaller than expected.");
+ }
+ }
+
+ public void increaseStackDepth() {
+ mValidator.increaseStackDepth();
+ }
+
+ public void decreaseStackDepth() {
+ mValidator.decreaseStackDepth();
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
new file mode 100644
index 0000000000..2ee2ae7493
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
@@ -0,0 +1,49 @@
+// 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.system.MojoException;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * A {@link ConnectionErrorHandler} that delegate the errors to a list of registered handlers. This
+ * class will use weak pointers to prevent keeping references to any handlers it delegates to.
+ */
+public class DelegatingConnectionErrorHandler implements ConnectionErrorHandler {
+
+ /**
+ * The registered handlers. This uses a {@link WeakHashMap} so that it doesn't prevent the
+ * handler from being garbage collected.
+ */
+ private final Set<ConnectionErrorHandler> mHandlers =
+ Collections.newSetFromMap(new WeakHashMap<ConnectionErrorHandler, Boolean>());
+
+ /**
+ * @see ConnectionErrorHandler#onConnectionError(MojoException)
+ */
+ @Override
+ public void onConnectionError(MojoException e) {
+ for (ConnectionErrorHandler handler : mHandlers) {
+ handler.onConnectionError(e);
+ }
+ }
+
+ /**
+ * Add a handler that will be notified of any error this object receives.
+ */
+ public void addConnectionErrorHandler(ConnectionErrorHandler handler) {
+ mHandlers.add(handler);
+ }
+
+ /**
+ * Remove a previously registered handler.
+ */
+ public void removeConnectionErrorHandler(ConnectionErrorHandler handler) {
+ mHandlers.remove(handler);
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
new file mode 100644
index 0000000000..eeb511c520
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
@@ -0,0 +1,26 @@
+// 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;
+
+/**
+ * Error when deserializing a mojo message.
+ */
+public class DeserializationException extends RuntimeException {
+
+ /**
+ * Constructs a new deserialization exception with the specified detail message.
+ */
+ public DeserializationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new deserialization exception with the specified cause.
+ */
+ public DeserializationException(Exception cause) {
+ super(cause);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
new file mode 100644
index 0000000000..3c86a8daa4
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
@@ -0,0 +1,587 @@
+// 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.Interface.Proxy.Handler;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.Pair;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed.
+ * It also keeps track of the associated handles, and the offset of the current data section.
+ */
+public class Encoder {
+
+ /**
+ * Container class for all state that must be shared between the main encoder and any used sub
+ * encoder.
+ */
+ private static class EncoderState {
+
+ /**
+ * The core used to encode interfaces.
+ */
+ public final Core core;
+
+ /**
+ * The ByteBuffer to which the message will be encoded.
+ */
+ public ByteBuffer byteBuffer;
+
+ /**
+ * The list of encountered handles.
+ */
+ public final List<Handle> handles = new ArrayList<Handle>();
+
+ /**
+ * The current absolute position for the next data section.
+ */
+ public int dataEnd;
+
+ /**
+ * @param core the |Core| implementation used to generate handles. Only used if the data
+ * structure being encoded contains interfaces, can be |null| otherwise.
+ * @param bufferSize A hint on the size of the message. Used to build the initial byte
+ * buffer.
+ */
+ private EncoderState(Core core, int bufferSize) {
+ assert bufferSize % BindingsHelper.ALIGNMENT == 0;
+ this.core = core;
+ byteBuffer = ByteBuffer.allocateDirect(
+ bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ dataEnd = 0;
+ }
+
+ /**
+ * Claim the given amount of memory at the end of the buffer, resizing it if needed.
+ */
+ public void claimMemory(int size) {
+ dataEnd += size;
+ growIfNeeded();
+ }
+
+ /**
+ * Grow the associated ByteBuffer if needed.
+ */
+ private void growIfNeeded() {
+ if (byteBuffer.capacity() >= dataEnd) {
+ return;
+ }
+ int targetSize = byteBuffer.capacity() * 2;
+ while (targetSize < dataEnd) {
+ targetSize *= 2;
+ }
+ ByteBuffer newBuffer = ByteBuffer.allocateDirect(targetSize);
+ newBuffer.order(ByteOrder.nativeOrder());
+ byteBuffer.position(0);
+ byteBuffer.limit(byteBuffer.capacity());
+ newBuffer.put(byteBuffer);
+ byteBuffer = newBuffer;
+ }
+ }
+
+ /**
+ * Default initial size of the data buffer. This must be a multiple of 8 bytes.
+ */
+ private static final int INITIAL_BUFFER_SIZE = 1024;
+
+ /**
+ * Base offset in the byte buffer for writing.
+ */
+ private int mBaseOffset;
+
+ /**
+ * The encoder state shared by the main encoder and all its sub-encoder.
+ */
+ private final EncoderState mEncoderState;
+
+ /**
+ * Returns the result message.
+ */
+ public Message getMessage() {
+ mEncoderState.byteBuffer.position(0);
+ mEncoderState.byteBuffer.limit(mEncoderState.dataEnd);
+ return new Message(mEncoderState.byteBuffer, mEncoderState.handles);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param core the |Core| implementation used to generate handles. Only used if the data
+ * structure being encoded contains interfaces, can be |null| otherwise.
+ * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer.
+ */
+ public Encoder(Core core, int sizeHint) {
+ this(new EncoderState(core, sizeHint));
+ }
+
+ /**
+ * Private constructor for sub-encoders.
+ */
+ private Encoder(EncoderState bufferInformation) {
+ mEncoderState = bufferInformation;
+ mBaseOffset = bufferInformation.dataEnd;
+ }
+
+ /**
+ * Returns a new encoder that will append to the current buffer.
+ */
+ public Encoder getEncoderAtDataOffset(DataHeader dataHeader) {
+ Encoder result = new Encoder(mEncoderState);
+ result.encode(dataHeader);
+ return result;
+ }
+
+ /**
+ * Encode a {@link DataHeader} and claim the amount of memory required for the data section
+ * (resizing the buffer if required).
+ */
+ public void encode(DataHeader s) {
+ mEncoderState.claimMemory(BindingsHelper.align(s.size));
+ encode(s.size, DataHeader.SIZE_OFFSET);
+ encode(s.elementsOrVersion, DataHeader.ELEMENTS_OR_VERSION_OFFSET);
+ }
+
+ /**
+ * Encode a byte at the given offset.
+ */
+ public void encode(byte v, int offset) {
+ mEncoderState.byteBuffer.put(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a boolean at the given offset.
+ */
+ public void encode(boolean v, int offset, int bit) {
+ if (v) {
+ byte encodedValue = mEncoderState.byteBuffer.get(mBaseOffset + offset);
+ encodedValue |= (byte) (1 << bit);
+ mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue);
+ }
+ }
+
+ /**
+ * Encode a short at the given offset.
+ */
+ public void encode(short v, int offset) {
+ mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode an int at the given offset.
+ */
+ public void encode(int v, int offset) {
+ mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a float at the given offset.
+ */
+ public void encode(float v, int offset) {
+ mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a long at the given offset.
+ */
+ public void encode(long v, int offset) {
+ mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a double at the given offset.
+ */
+ public void encode(double v, int offset) {
+ mEncoderState.byteBuffer.putDouble(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a {@link Struct} at the given offset.
+ */
+ public void encode(Struct v, int offset, boolean nullable) {
+ if (v == null) {
+ encodeNullPointer(offset, nullable);
+ return;
+ }
+ encodePointerToNextUnclaimedData(offset);
+ v.encode(this);
+ }
+
+ /**
+ * Encode a {@link Union} at the given offset.
+ */
+ public void encode(Union v, int offset, boolean nullable) {
+ if (v == null && !nullable) {
+ throw new SerializationException(
+ "Trying to encode a null pointer for a non-nullable type.");
+ }
+ if (v == null) {
+ encode(0L, offset);
+ encode(0L, offset + DataHeader.HEADER_SIZE);
+ return;
+ }
+ v.encode(this, offset);
+ }
+
+ /**
+ * Encodes a String.
+ */
+ public void encode(String v, int offset, boolean nullable) {
+ if (v == null) {
+ encodeNullPointer(offset, nullable);
+ return;
+ }
+ final int arrayNullability = nullable
+ ? BindingsHelper.ARRAY_NULLABLE : BindingsHelper.NOTHING_NULLABLE;
+ encode(v.getBytes(Charset.forName("utf8")), offset, arrayNullability,
+ BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
+ }
+
+ /**
+ * Encodes a {@link Handle}.
+ */
+ public void encode(Handle v, int offset, boolean nullable) {
+ if (v == null || !v.isValid()) {
+ encodeInvalidHandle(offset, nullable);
+ } else {
+ encode(mEncoderState.handles.size(), offset);
+ mEncoderState.handles.add(v);
+ }
+ }
+
+ /**
+ * Encode an {@link Interface}.
+ */
+ public <T extends Interface> void encode(T v, int offset, boolean nullable,
+ Interface.Manager<T, ?> manager) {
+ if (v == null) {
+ encodeInvalidHandle(offset, nullable);
+ encode(0, offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+ return;
+ }
+ if (mEncoderState.core == null) {
+ throw new UnsupportedOperationException(
+ "The encoder has been created without a Core. It can't encode an interface.");
+ }
+ // If the instance is a proxy, pass the proxy's handle instead of creating a new stub.
+ if (v instanceof Interface.Proxy) {
+ Handler handler = ((Interface.Proxy) v).getProxyHandler();
+ encode(handler.passHandle(), offset, nullable);
+ encode(handler.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+ return;
+ }
+ Pair<MessagePipeHandle, MessagePipeHandle> handles =
+ mEncoderState.core.createMessagePipe(null);
+ manager.bind(v, handles.first);
+ encode(handles.second, offset, nullable);
+ encode(manager.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+ }
+
+ /**
+ * Encode an {@link InterfaceRequest}.
+ */
+ public <I extends Interface> void encode(InterfaceRequest<I> v, int offset, boolean nullable) {
+ if (v == null) {
+ encodeInvalidHandle(offset, nullable);
+ return;
+ }
+ if (mEncoderState.core == null) {
+ throw new UnsupportedOperationException(
+ "The encoder has been created without a Core. It can't encode an interface.");
+ }
+ encode(v.passHandle(), offset, nullable);
+ }
+
+ /**
+ * Encode an associated interface. Not yet supported.
+ */
+ public void encode(AssociatedInterfaceNotSupported v, int offset, boolean nullable) {
+ }
+
+ /**
+ * Encode an associated interface request. Not yet supported.
+ */
+ public void encode(AssociatedInterfaceRequestNotSupported v, int offset, boolean nullable) {
+ }
+
+ /**
+ * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length.
+ */
+ public Encoder encodePointerArray(int length, int offset, int expectedLength) {
+ return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset, expectedLength);
+ }
+
+ /**
+ * Returns an {@link Encoder} suitable for encoding an array of union of the given length.
+ */
+ public Encoder encodeUnionArray(int length, int offset, int expectedLength) {
+ return encoderForArray(BindingsHelper.UNION_SIZE, length, offset, expectedLength);
+ }
+
+ /**
+ * Encodes an array of booleans.
+ */
+ public void encode(boolean[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+ && expectedLength != v.length) {
+ throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+ }
+ byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT];
+ for (int i = 0; i < bytes.length; ++i) {
+ for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
+ int booleanIndex = BindingsHelper.ALIGNMENT * i + j;
+ if (booleanIndex < v.length && v[booleanIndex]) {
+ bytes[i] |= (byte) (1 << j);
+ }
+ }
+ }
+ encodeByteArray(bytes, v.length, offset);
+ }
+
+ /**
+ * Encodes an array of bytes.
+ */
+ public void encode(byte[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+ && expectedLength != v.length) {
+ throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+ }
+ encodeByteArray(v, v.length, offset);
+ }
+
+ /**
+ * Encodes an array of shorts.
+ */
+ public void encode(short[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(2, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of ints.
+ */
+ public void encode(int[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(4, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of floats.
+ */
+ public void encode(float[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(4, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of longs.
+ */
+ public void encode(long[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(8, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of doubles.
+ */
+ public void encode(double[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(8, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of {@link Handle}.
+ */
+ public void encode(Handle[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ Encoder e = encoderForArray(
+ BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+ for (int i = 0; i < v.length; ++i) {
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ }
+
+ /**
+ * Encodes an array of {@link Interface}.
+ */
+ public <T extends Interface> void encode(T[] v, int offset, int arrayNullability,
+ int expectedLength, Interface.Manager<T, ?> manager) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ Encoder e = encoderForArray(
+ BindingsHelper.SERIALIZED_INTERFACE_SIZE, v.length, offset, expectedLength);
+ for (int i = 0; i < v.length; ++i) {
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_INTERFACE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability), manager);
+ }
+ }
+
+ public Encoder encoderForMap(int offset) {
+ encodePointerToNextUnclaimedData(offset);
+ return getEncoderAtDataOffset(BindingsHelper.MAP_STRUCT_HEADER);
+ }
+
+ /**
+ * Encodes a pointer to the next unclaimed memory and returns an encoder suitable to encode an
+ * union at this location.
+ */
+ public Encoder encoderForUnionPointer(int offset) {
+ encodePointerToNextUnclaimedData(offset);
+ Encoder result = new Encoder(mEncoderState);
+ result.mEncoderState.claimMemory(16);
+ return result;
+ }
+
+ /**
+ * Encodes an array of {@link InterfaceRequest}.
+ */
+ public <I extends Interface> void encode(InterfaceRequest<I>[] v, int offset,
+ int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ Encoder e = encoderForArray(
+ BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+ for (int i = 0; i < v.length; ++i) {
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ }
+
+ /**
+ * Encodes an array of associated interfaces. Not yet supported.
+ */
+ public void encode(AssociatedInterfaceNotSupported[] v, int offset, int arrayNullability,
+ int expectedLength) {}
+
+ /**
+ * Encodes an array of associated interface requests. Not yet supported.
+ */
+ public void encode(AssociatedInterfaceRequestNotSupported[] v, int offset, int arrayNullability,
+ int expectedLength) {}
+
+ /**
+ * Encodes a <code>null</code> pointer iff the object is nullable, raises an exception
+ * otherwise.
+ */
+ public void encodeNullPointer(int offset, boolean nullable) {
+ if (!nullable) {
+ throw new SerializationException(
+ "Trying to encode a null pointer for a non-nullable type.");
+ }
+ mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0);
+ }
+
+ /**
+ * Encodes an invalid handle iff the object is nullable, raises an exception otherwise.
+ */
+ public void encodeInvalidHandle(int offset, boolean nullable) {
+ if (!nullable) {
+ throw new SerializationException(
+ "Trying to encode an invalid handle for a non-nullable type.");
+ }
+ mEncoderState.byteBuffer.putInt(mBaseOffset + offset, -1);
+ }
+
+ /**
+ * Claim the given amount of memory at the end of the buffer, resizing it if needed.
+ */
+ void claimMemory(int size) {
+ mEncoderState.claimMemory(BindingsHelper.align(size));
+ }
+
+ private void encodePointerToNextUnclaimedData(int offset) {
+ encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset);
+ }
+
+ private Encoder encoderForArray(
+ int elementSizeInByte, int length, int offset, int expectedLength) {
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+ && expectedLength != length) {
+ throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+ }
+ return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset);
+ }
+
+ private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) {
+ encodePointerToNextUnclaimedData(offset);
+ return getEncoderAtDataOffset(
+ new DataHeader(DataHeader.HEADER_SIZE + byteSize, length));
+ }
+
+ private void encodeByteArray(byte[] bytes, int length, int offset) {
+ encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes);
+ }
+
+ private void append(byte[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.put(v);
+ }
+
+ private void append(short[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asShortBuffer().put(v);
+ }
+
+ private void append(int[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asIntBuffer().put(v);
+ }
+
+ private void append(float[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asFloatBuffer().put(v);
+ }
+
+ private void append(double[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asDoubleBuffer().put(v);
+ }
+
+ private void append(long[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asLongBuffer().put(v);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
new file mode 100644
index 0000000000..bb49cbc4bd
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
@@ -0,0 +1,169 @@
+// 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.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.ResultAnd;
+import org.chromium.mojo.system.Watcher;
+import org.chromium.mojo.system.Watcher.Callback;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A factory which provides per-thread executors, which enable execution on the thread from which
+ * they were obtained.
+ */
+class ExecutorFactory {
+
+ /**
+ * A null buffer which is used to send messages without any data on the PipedExecutor's
+ * signaling handles.
+ */
+ private static final ByteBuffer NOTIFY_BUFFER = null;
+
+ /**
+ * Implementation of the executor which uses a pair of {@link MessagePipeHandle} for signaling.
+ * The executor will wait asynchronously on one end of a {@link MessagePipeHandle} on the thread
+ * on which it was created. Other threads can call execute with a {@link Runnable}, and the
+ * executor will queue the {@link Runnable} and write a message on the other end of the handle.
+ * This will wake up the executor which is waiting on the handle, which will then dequeue the
+ * {@link Runnable} and execute it on the original thread.
+ */
+ private static class PipedExecutor implements Executor, Callback {
+
+ /**
+ * The handle which is written to. Access to this object must be protected with |mLock|.
+ */
+ private final MessagePipeHandle mWriteHandle;
+ /**
+ * The handle which is read from.
+ */
+ private final MessagePipeHandle mReadHandle;
+ /**
+ * The list of actions left to be run. Access to this object must be protected with |mLock|.
+ */
+ private final List<Runnable> mPendingActions;
+ /**
+ * Lock protecting access to |mWriteHandle| and |mPendingActions|.
+ */
+ private final Object mLock;
+ /**
+ * The {@link Watcher} to get notified of new message availability on |mReadHandle|.
+ */
+ private final Watcher mWatcher;
+
+ /**
+ * Constructor.
+ */
+ public PipedExecutor(Core core) {
+ mWatcher = core.getWatcher();
+ assert mWatcher != null;
+ mLock = new Object();
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(
+ new MessagePipeHandle.CreateOptions());
+ mReadHandle = handles.first;
+ mWriteHandle = handles.second;
+ mPendingActions = new ArrayList<Runnable>();
+ mWatcher.start(mReadHandle, Core.HandleSignals.READABLE, this);
+ }
+
+ /**
+ * @see Callback#onResult(int)
+ */
+ @Override
+ public void onResult(int result) {
+ if (result == MojoResult.OK && readNotifyBufferMessage()) {
+ runNextAction();
+ } else {
+ close();
+ }
+ }
+
+ /**
+ * Close the handles. Should only be called on the executor thread.
+ */
+ private void close() {
+ synchronized (mLock) {
+ mWriteHandle.close();
+ mPendingActions.clear();
+ }
+ mWatcher.cancel();
+ mWatcher.destroy();
+ mReadHandle.close();
+ }
+
+ /**
+ * Read the next message on |mReadHandle|, and return |true| if successful, |false|
+ * otherwise.
+ */
+ private boolean readNotifyBufferMessage() {
+ try {
+ ResultAnd<ReadMessageResult> readMessageResult =
+ mReadHandle.readMessage(NOTIFY_BUFFER, 0, MessagePipeHandle.ReadFlags.NONE);
+ if (readMessageResult.getMojoResult() == MojoResult.OK) {
+ return true;
+ }
+ } catch (MojoException e) {
+ // Will be closed by the fall back at the end of this method.
+ }
+ return false;
+ }
+
+ /**
+ * Run the next action in the |mPendingActions| queue.
+ */
+ private void runNextAction() {
+ Runnable toRun = null;
+ synchronized (mLock) {
+ toRun = mPendingActions.remove(0);
+ }
+ toRun.run();
+ }
+
+ /**
+ * Execute the given |command| in the executor thread. This can be called on any thread.
+ *
+ * @see Executor#execute(Runnable)
+ */
+ @Override
+ public void execute(Runnable command) {
+ // Accessing the write handle must be protected by the lock, because it can be closed
+ // from the executor's thread.
+ synchronized (mLock) {
+ if (!mWriteHandle.isValid()) {
+ throw new IllegalStateException(
+ "Trying to execute an action on a closed executor.");
+ }
+ mPendingActions.add(command);
+ mWriteHandle.writeMessage(NOTIFY_BUFFER, null, MessagePipeHandle.WriteFlags.NONE);
+ }
+ }
+ }
+
+ /**
+ * Keep one executor per executor thread.
+ */
+ private static final ThreadLocal<Executor> EXECUTORS = new ThreadLocal<Executor>();
+
+ /**
+ * Returns an {@link Executor} that will run all of its actions in the current thread.
+ */
+ public static Executor getExecutorForCurrentThread(Core core) {
+ Executor executor = EXECUTORS.get();
+ if (executor == null) {
+ executor = new PipedExecutor(core);
+ EXECUTORS.set(executor);
+ }
+ return executor;
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
new file mode 100644
index 0000000000..60fc33ba4e
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
@@ -0,0 +1,29 @@
+// 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.system.Handle;
+
+import java.io.Closeable;
+
+/**
+ * Describes a class that owns a handle.
+ *
+ * @param <H> The type of the owned handle.
+ */
+public interface HandleOwner<H extends Handle> extends Closeable {
+
+ /**
+ * Pass the handle owned by this class.
+ */
+ public H passHandle();
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close();
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
new file mode 100644
index 0000000000..2699ab899a
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
@@ -0,0 +1,425 @@
+// 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);
+
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
new file mode 100644
index 0000000000..51f543d567
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
@@ -0,0 +1,105 @@
+// Copyright 2015 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.Manager;
+import org.chromium.mojo.bindings.Interface.Proxy;
+import org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants;
+import org.chromium.mojo.bindings.interfacecontrol.QueryVersionResult;
+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;
+
+/**
+ * Helper class to handle interface control messages. See
+ * mojo/public/interfaces/bindings/interface_control_messages.mojom.
+ */
+public class InterfaceControlMessagesHelper {
+ /**
+ * MessageReceiver that forwards a message containing a {@link RunResponseMessageParams} to a
+ * callback.
+ */
+ private static class RunResponseForwardToCallback
+ extends SideEffectFreeCloseable implements MessageReceiver {
+ private final Callback1<RunResponseMessageParams> mCallback;
+
+ RunResponseForwardToCallback(Callback1<RunResponseMessageParams> callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ RunResponseMessageParams response =
+ RunResponseMessageParams.deserialize(message.asServiceMessage().getPayload());
+ mCallback.call(response);
+ return true;
+ }
+ }
+
+ /**
+ * Sends the given run message through the receiver, registering the callback.
+ */
+ public static void sendRunMessage(Core core, MessageReceiverWithResponder receiver,
+ RunMessageParams params, Callback1<RunResponseMessageParams> callback) {
+ Message message = params.serializeWithHeader(
+ core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID,
+ MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0));
+ receiver.acceptWithResponder(message, new RunResponseForwardToCallback(callback));
+ }
+
+ /**
+ * Sends the given run or close pipe message through the receiver.
+ */
+ public static void sendRunOrClosePipeMessage(
+ Core core, MessageReceiverWithResponder receiver, RunOrClosePipeMessageParams params) {
+ Message message = params.serializeWithHeader(core,
+ new MessageHeader(InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID));
+ receiver.accept(message);
+ }
+
+ /**
+ * Handles a received run message.
+ */
+ public static <I extends Interface, P extends Proxy> boolean handleRun(
+ Core core, Manager<I, P> manager, ServiceMessage message, MessageReceiver responder) {
+ Message payload = message.getPayload();
+ RunMessageParams query = RunMessageParams.deserialize(payload);
+ RunResponseMessageParams response = new RunResponseMessageParams();
+ response.output = new RunOutput();
+ if (query.input.which() == RunInput.Tag.QueryVersion) {
+ response.output.setQueryVersionResult(new QueryVersionResult());
+ response.output.getQueryVersionResult().version = manager.getVersion();
+ } else {
+ response.output = null;
+ }
+
+ return responder.accept(response.serializeWithHeader(
+ core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID,
+ MessageHeader.MESSAGE_IS_RESPONSE_FLAG,
+ message.getHeader().getRequestId())));
+ }
+
+ /**
+ * Handles a received run or close pipe message. Closing the pipe is handled by returning
+ * |false|.
+ */
+ public static <I extends Interface, P extends Proxy> boolean handleRunOrClosePipe(
+ Manager<I, P> manager, ServiceMessage message) {
+ Message payload = message.getPayload();
+ RunOrClosePipeMessageParams query = RunOrClosePipeMessageParams.deserialize(payload);
+ if (query.input.which() == RunOrClosePipeInput.Tag.RequireVersion) {
+ return query.input.getRequireVersion().version <= manager.getVersion();
+ }
+ return false;
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
new file mode 100644
index 0000000000..61899b4467
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
@@ -0,0 +1,58 @@
+// 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.system.MessagePipeHandle;
+
+/**
+ * One end of the message pipe representing a request to create an implementation to be bound to it.
+ * The other end of the pipe is bound to a proxy, which can be used immediately, while the
+ * InterfaceRequest is being sent.
+ * <p>
+ * InterfaceRequest are built using |Interface.Manager|.
+ *
+ * @param <P> the type of the remote interface proxy.
+ */
+public class InterfaceRequest<P extends Interface> implements HandleOwner<MessagePipeHandle> {
+
+ /**
+ * The handle which will be sent and will be connected to the implementation.
+ */
+ private final MessagePipeHandle mHandle;
+
+ /**
+ * Constructor.
+ *
+ * @param handle the handle which will be sent and will be connected to the implementation.
+ */
+ InterfaceRequest(MessagePipeHandle handle) {
+ mHandle = handle;
+ }
+
+ /**
+ * @see HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ return mHandle.pass();
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ mHandle.close();
+ }
+
+ /**
+ * Returns an {@link InterfaceRequest} that wraps the given handle. This method is not type safe
+ * and should be avoided unless absolutely necessary.
+ */
+ @SuppressWarnings("rawtypes")
+ public static InterfaceRequest asInterfaceRequestUnsafe(MessagePipeHandle handle) {
+ return new InterfaceRequest(handle);
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
new file mode 100644
index 0000000000..996c457338
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
@@ -0,0 +1,69 @@
+// 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.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A raw message to be sent/received from a {@link MessagePipeHandle}. Note that this can contain
+ * any data, not necessarily a Mojo message with a proper header. See also {@link ServiceMessage}.
+ */
+public class Message {
+
+ /**
+ * The data of the message.
+ */
+ private final ByteBuffer mBuffer;
+
+ /**
+ * The handles of the message.
+ */
+ private final List<? extends Handle> mHandle;
+
+ /**
+ * This message interpreted as a message for a mojo service with an appropriate header.
+ */
+ private ServiceMessage mWithHeader;
+
+ /**
+ * Constructor.
+ *
+ * @param buffer The buffer containing the bytes to send. This must be a direct buffer.
+ * @param handles The list of handles to send.
+ */
+ public Message(ByteBuffer buffer, List<? extends Handle> handles) {
+ assert buffer.isDirect();
+ mBuffer = buffer;
+ mHandle = handles;
+ }
+
+ /**
+ * The data of the message.
+ */
+ public ByteBuffer getData() {
+ return mBuffer;
+ }
+
+ /**
+ * The handles of the message.
+ */
+ public List<? extends Handle> getHandles() {
+ return mHandle;
+ }
+
+ /**
+ * Returns the message interpreted as a message for a mojo service.
+ */
+ public ServiceMessage asServiceMessage() {
+ if (mWithHeader == null) {
+ mWithHeader = new ServiceMessage(this);
+ }
+ return mWithHeader;
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
new file mode 100644
index 0000000000..771d22b5c3
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
@@ -0,0 +1,248 @@
+// 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 java.nio.ByteBuffer;
+
+/**
+ * Header information for a message.
+ */
+public class MessageHeader {
+
+ private static final int SIMPLE_MESSAGE_SIZE = 24;
+ private static final int SIMPLE_MESSAGE_VERSION = 0;
+ private static final DataHeader SIMPLE_MESSAGE_STRUCT_INFO =
+ new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_VERSION);
+
+ private static final int MESSAGE_WITH_REQUEST_ID_SIZE = 32;
+ private static final int MESSAGE_WITH_REQUEST_ID_VERSION = 1;
+ private static final DataHeader MESSAGE_WITH_REQUEST_ID_STRUCT_INFO =
+ new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_VERSION);
+
+ private static final int INTERFACE_ID_OFFSET = 8;
+ private static final int TYPE_OFFSET = 12;
+ private static final int FLAGS_OFFSET = 16;
+ private static final int REQUEST_ID_OFFSET = 24;
+
+ /**
+ * Flag for a header of a simple message.
+ */
+ public static final int NO_FLAG = 0;
+
+ /**
+ * Flag for a header of a message that expected a response.
+ */
+ public static final int MESSAGE_EXPECTS_RESPONSE_FLAG = 1 << 0;
+
+ /**
+ * Flag for a header of a message that is a response.
+ */
+ public static final int MESSAGE_IS_RESPONSE_FLAG = 1 << 1;
+
+ private final DataHeader mDataHeader;
+ private final int mType;
+ private final int mFlags;
+ private long mRequestId;
+
+ /**
+ * Constructor for the header of a message which does not have a response.
+ */
+ public MessageHeader(int type) {
+ mDataHeader = SIMPLE_MESSAGE_STRUCT_INFO;
+ mType = type;
+ mFlags = 0;
+ mRequestId = 0;
+ }
+
+ /**
+ * Constructor for the header of a message which have a response or being itself a response.
+ */
+ public MessageHeader(int type, int flags, long requestId) {
+ assert mustHaveRequestId(flags);
+ mDataHeader = MESSAGE_WITH_REQUEST_ID_STRUCT_INFO;
+ mType = type;
+ mFlags = flags;
+ mRequestId = requestId;
+ }
+
+ /**
+ * Constructor, parsing the header from a message. Should only be used by {@link Message}
+ * itself.
+ */
+ MessageHeader(Message message) {
+ Decoder decoder = new Decoder(message);
+ mDataHeader = decoder.readDataHeader();
+ validateDataHeader(mDataHeader);
+
+ // Currently associated interfaces are not supported.
+ int interfaceId = decoder.readInt(INTERFACE_ID_OFFSET);
+ if (interfaceId != 0) {
+ throw new DeserializationException("Non-zero interface ID, expecting zero since "
+ + "associated interfaces are not yet supported.");
+ }
+
+ mType = decoder.readInt(TYPE_OFFSET);
+ mFlags = decoder.readInt(FLAGS_OFFSET);
+ if (mustHaveRequestId(mFlags)) {
+ if (mDataHeader.size < MESSAGE_WITH_REQUEST_ID_SIZE) {
+ throw new DeserializationException("Incorrect message size, expecting at least "
+ + MESSAGE_WITH_REQUEST_ID_SIZE
+ + " for a message with a request identifier, but got: " + mDataHeader.size);
+
+ }
+ mRequestId = decoder.readLong(REQUEST_ID_OFFSET);
+ } else {
+ mRequestId = 0;
+ }
+ }
+
+ /**
+ * Returns the size in bytes of the serialization of the header.
+ */
+ public int getSize() {
+ return mDataHeader.size;
+ }
+
+ /**
+ * Returns the type of the message.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the flags associated to the message.
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns if the message has the given flag.
+ */
+ public boolean hasFlag(int flag) {
+ return (mFlags & flag) == flag;
+ }
+
+ /**
+ * Returns if the message has a request id.
+ */
+ public boolean hasRequestId() {
+ return mustHaveRequestId(mFlags);
+ }
+
+ /**
+ * Return the request id for the message. Must only be called if the message has a request id.
+ */
+ public long getRequestId() {
+ assert hasRequestId();
+ return mRequestId;
+ }
+
+ /**
+ * Encode the header.
+ */
+ public void encode(Encoder encoder) {
+ encoder.encode(mDataHeader);
+ // Set the interface ID field to 0.
+ encoder.encode(0, INTERFACE_ID_OFFSET);
+ encoder.encode(getType(), TYPE_OFFSET);
+ encoder.encode(getFlags(), FLAGS_OFFSET);
+ if (hasRequestId()) {
+ encoder.encode(getRequestId(), REQUEST_ID_OFFSET);
+ }
+ }
+
+ /**
+ * Returns true if the header has the expected flags. Only considers flags this class knows
+ * about in order to allow this class to work with future version of the header format.
+ */
+ public boolean validateHeader(int expectedFlags) {
+ int knownFlags = getFlags() & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG);
+ return knownFlags == expectedFlags;
+ }
+
+ /**
+ * Returns true if the header has the expected type and flags. Only consider flags this class
+ * knows about in order to allow this class to work with future version of the header format.
+ */
+ public boolean validateHeader(int expectedType, int expectedFlags) {
+ return getType() == expectedType && validateHeader(expectedFlags);
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mDataHeader == null) ? 0 : mDataHeader.hashCode());
+ result = prime * result + mFlags;
+ result = prime * result + (int) (mRequestId ^ (mRequestId >>> 32));
+ result = prime * result + mType;
+ return result;
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) return true;
+ if (object == null) return false;
+ if (getClass() != object.getClass()) return false;
+
+ MessageHeader other = (MessageHeader) object;
+ return (BindingsHelper.equals(mDataHeader, other.mDataHeader)
+ && mFlags == other.mFlags
+ && mRequestId == other.mRequestId
+ && mType == other.mType);
+ }
+
+ /**
+ * Set the request id on the message contained in the given buffer.
+ */
+ void setRequestId(ByteBuffer buffer, long requestId) {
+ assert mustHaveRequestId(buffer.getInt(FLAGS_OFFSET));
+ buffer.putLong(REQUEST_ID_OFFSET, requestId);
+ mRequestId = requestId;
+ }
+
+ /**
+ * Returns whether a message with the given flags must have a request Id.
+ */
+ private static boolean mustHaveRequestId(int flags) {
+ return (flags & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG)) != 0;
+ }
+
+ /**
+ * Validate that the given {@link DataHeader} can be the data header of a message header.
+ */
+ private static void validateDataHeader(DataHeader dataHeader) {
+ if (dataHeader.elementsOrVersion < SIMPLE_MESSAGE_VERSION) {
+ throw new DeserializationException("Incorrect number of fields, expecting at least "
+ + SIMPLE_MESSAGE_VERSION + ", but got: " + dataHeader.elementsOrVersion);
+ }
+ if (dataHeader.size < SIMPLE_MESSAGE_SIZE) {
+ throw new DeserializationException(
+ "Incorrect message size, expecting at least " + SIMPLE_MESSAGE_SIZE
+ + ", but got: " + dataHeader.size);
+ }
+ if (dataHeader.elementsOrVersion == SIMPLE_MESSAGE_VERSION
+ && dataHeader.size != SIMPLE_MESSAGE_SIZE) {
+ throw new DeserializationException("Incorrect message size for a message with "
+ + SIMPLE_MESSAGE_VERSION + " fields, expecting " + SIMPLE_MESSAGE_SIZE
+ + ", but got: " + dataHeader.size);
+ }
+ if (dataHeader.elementsOrVersion == MESSAGE_WITH_REQUEST_ID_VERSION
+ && dataHeader.size != MESSAGE_WITH_REQUEST_ID_SIZE) {
+ throw new DeserializationException("Incorrect message size for a message with "
+ + MESSAGE_WITH_REQUEST_ID_VERSION + " fields, expecting "
+ + MESSAGE_WITH_REQUEST_ID_SIZE + ", but got: " + dataHeader.size);
+ }
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
new file mode 100644
index 0000000000..4223297914
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
@@ -0,0 +1,25 @@
+// 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 java.io.Closeable;
+
+/**
+ * A class which implements this interface can receive {@link Message} objects.
+ */
+public interface MessageReceiver extends Closeable {
+
+ /**
+ * Receive a {@link Message}. The {@link MessageReceiver} is allowed to mutate the message.
+ * Returns |true| if the message has been handled, |false| otherwise.
+ */
+ boolean accept(Message message);
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close();
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
new file mode 100644
index 0000000000..e24a5586c6
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
@@ -0,0 +1,21 @@
+// 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;
+
+/**
+ * A {@link MessageReceiver} that can also handle the handle the response message generated from the
+ * given message.
+ */
+public interface MessageReceiverWithResponder extends MessageReceiver {
+
+ /**
+ * A variant on {@link #accept(Message)} that registers a {@link MessageReceiver}
+ * (known as the responder) to handle the response message generated from the given message. The
+ * responder's {@link #accept(Message)} method may be called as part of the call to
+ * {@link #acceptWithResponder(Message, MessageReceiver)}, or some time after its
+ * return.
+ */
+ boolean acceptWithResponder(Message message, MessageReceiver responder);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
new file mode 100644
index 0000000000..ba19ae5ac4
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
@@ -0,0 +1,31 @@
+// 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.system.MessagePipeHandle;
+
+/**
+ * A {@link Router} will handle mojo message and forward those to a {@link Connector}. It deals with
+ * parsing of headers and adding of request ids in order to be able to match a response to a
+ * request.
+ */
+public interface Router extends MessageReceiverWithResponder, HandleOwner<MessagePipeHandle> {
+
+ /**
+ * Start listening for incoming messages.
+ */
+ public void start();
+
+ /**
+ * Set the {@link MessageReceiverWithResponder} that will deserialize and use the message
+ * received from the pipe.
+ */
+ public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver);
+
+ /**
+ * Set the handle that will be notified of errors on the message pipe.
+ */
+ public void setErrorHandler(ConnectionErrorHandler errorHandler);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
new file mode 100644
index 0000000000..aebc9e21c8
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
@@ -0,0 +1,274 @@
+// 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 android.annotation.SuppressLint;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.Watcher;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of {@link Router}.
+ */
+@SuppressLint("UseSparseArrays") // https://crbug.com/600699
+public class RouterImpl implements Router {
+
+ /**
+ * {@link MessageReceiver} used as the {@link Connector} callback.
+ */
+ private class HandleIncomingMessageThunk implements MessageReceiver {
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ return handleIncomingMessage(message);
+ }
+
+ /**
+ * @see MessageReceiver#close()
+ */
+ @Override
+ public void close() {
+ handleConnectorClose();
+ }
+
+ }
+
+ /**
+ *
+ * {@link MessageReceiver} used to return responses to the caller.
+ */
+ class ResponderThunk implements MessageReceiver {
+ private boolean mAcceptWasInvoked;
+
+ /**
+ * @see
+ * MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ mAcceptWasInvoked = true;
+ return RouterImpl.this.accept(message);
+ }
+
+ /**
+ * @see MessageReceiver#close()
+ */
+ @Override
+ public void close() {
+ RouterImpl.this.close();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (!mAcceptWasInvoked) {
+ // We close the pipe here as a way of signaling to the calling application that an
+ // error condition occurred. Without this the calling application would have no
+ // way of knowing it should stop waiting for a response.
+ RouterImpl.this.closeOnHandleThread();
+ }
+ super.finalize();
+ }
+ }
+
+ /**
+ * The {@link Connector} which is connected to the handle.
+ */
+ private final Connector mConnector;
+
+ /**
+ * The {@link MessageReceiverWithResponder} that will consume the messages received from the
+ * pipe.
+ */
+ private MessageReceiverWithResponder mIncomingMessageReceiver;
+
+ /**
+ * The next id to use for a request id which needs a response. It is auto-incremented.
+ */
+ private long mNextRequestId = 1;
+
+ /**
+ * The map from request ids to {@link MessageReceiver} of request currently in flight.
+ */
+ private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageReceiver>();
+
+ /**
+ * An Executor that will run on the thread associated with the MessagePipe to which
+ * this Router is bound. This may be {@code Null} if the MessagePipeHandle passed
+ * in to the constructor is not valid.
+ */
+ private final Executor mExecutor;
+
+ /**
+ * Constructor that will use the default {@link Watcher}.
+ *
+ * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
+ */
+ public RouterImpl(MessagePipeHandle messagePipeHandle) {
+ this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
+ * @param watcher the {@link Watcher} to use to get notification of new messages on the
+ * handle.
+ */
+ public RouterImpl(MessagePipeHandle messagePipeHandle, Watcher watcher) {
+ mConnector = new Connector(messagePipeHandle, watcher);
+ mConnector.setIncomingMessageReceiver(new HandleIncomingMessageThunk());
+ Core core = messagePipeHandle.getCore();
+ if (core != null) {
+ mExecutor = ExecutorFactory.getExecutorForCurrentThread(core);
+ } else {
+ mExecutor = null;
+ }
+ }
+
+ /**
+ * @see org.chromium.mojo.bindings.Router#start()
+ */
+ @Override
+ public void start() {
+ mConnector.start();
+ }
+
+ /**
+ * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
+ */
+ @Override
+ public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
+ this.mIncomingMessageReceiver = incomingMessageReceiver;
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ // A message without responder is directly forwarded to the connector.
+ return mConnector.accept(message);
+ }
+
+ /**
+ * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+ */
+ @Override
+ public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+ // The message must have a header.
+ ServiceMessage messageWithHeader = message.asServiceMessage();
+ // Checking the message expects a response.
+ assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG);
+
+ // Compute a request id for being able to route the response.
+ // TODO(lhchavez): Remove this hack. See b/28986534 for details.
+ synchronized (mResponders) {
+ long requestId = mNextRequestId++;
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ if (requestId == 0) {
+ requestId = mNextRequestId++;
+ }
+ if (mResponders.containsKey(requestId)) {
+ throw new IllegalStateException("Unable to find a new request identifier.");
+ }
+ messageWithHeader.setRequestId(requestId);
+ if (!mConnector.accept(messageWithHeader)) {
+ return false;
+ }
+ // Only keep the responder is the message has been accepted.
+ mResponders.put(requestId, responder);
+ }
+ return true;
+ }
+
+ /**
+ * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ return mConnector.passHandle();
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ mConnector.close();
+ }
+
+ /**
+ * @see Router#setErrorHandler(ConnectionErrorHandler)
+ */
+ @Override
+ public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+ mConnector.setErrorHandler(errorHandler);
+ }
+
+ /**
+ * Receive a message from the connector. Returns |true| if the message has been handled.
+ */
+ private boolean handleIncomingMessage(Message message) {
+ MessageHeader header = message.asServiceMessage().getHeader();
+ if (header.hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG)) {
+ if (mIncomingMessageReceiver != null) {
+ return mIncomingMessageReceiver.acceptWithResponder(message, new ResponderThunk());
+ }
+ // If we receive a request expecting a response when the client is not
+ // listening, then we have no choice but to tear down the pipe.
+ close();
+ return false;
+ } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
+ long requestId = header.getRequestId();
+ MessageReceiver responder;
+ // TODO(lhchavez): Remove this hack. See b/28986534 for details.
+ synchronized (mResponders) {
+ responder = mResponders.get(requestId);
+ if (responder == null) {
+ return false;
+ }
+ mResponders.remove(requestId);
+ }
+ return responder.accept(message);
+ } else {
+ if (mIncomingMessageReceiver != null) {
+ return mIncomingMessageReceiver.accept(message);
+ }
+ // OK to drop the message.
+ }
+ return false;
+ }
+
+ private void handleConnectorClose() {
+ if (mIncomingMessageReceiver != null) {
+ mIncomingMessageReceiver.close();
+ }
+ }
+
+ /**
+ * Invokes {@link #close()} asynchronously on the thread associated with
+ * this Router's Handle. If this Router was constructed with an invalid
+ * handle then this method does nothing.
+ */
+ private void closeOnHandleThread() {
+ if (mExecutor != null) {
+ mExecutor.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ close();
+ }
+ });
+ }
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
new file mode 100644
index 0000000000..d4f550227f
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
@@ -0,0 +1,26 @@
+// 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;
+
+/**
+ * Error that can be thrown when serializing a mojo message.
+ */
+public class SerializationException extends RuntimeException {
+
+ /**
+ * Constructs a new serialization exception with the specified detail message.
+ */
+ public SerializationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new serialization exception with the specified cause.
+ */
+ public SerializationException(Exception cause) {
+ super(cause);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
new file mode 100644
index 0000000000..313dc6aea3
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
@@ -0,0 +1,73 @@
+// 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 java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Represents a {@link Message} which contains a {@link MessageHeader}. Deals with parsing the
+ * {@link MessageHeader} for a message.
+ */
+public class ServiceMessage extends Message {
+
+ private final MessageHeader mHeader;
+ private Message mPayload;
+
+ /**
+ * Reinterpret the given |message| as a message with the given |header|. The |message| must
+ * contain the |header| as the start of its raw data.
+ */
+ public ServiceMessage(Message baseMessage, MessageHeader header) {
+ super(baseMessage.getData(), baseMessage.getHandles());
+ assert header.equals(new org.chromium.mojo.bindings.MessageHeader(baseMessage));
+ this.mHeader = header;
+ }
+
+ /**
+ * Reinterpret the given |message| as a message with a header. The |message| must contain a
+ * header as the start of it's raw data, which will be parsed by this constructor.
+ */
+ ServiceMessage(Message baseMessage) {
+ this(baseMessage, new org.chromium.mojo.bindings.MessageHeader(baseMessage));
+ }
+
+ /**
+ * @see Message#asServiceMessage()
+ */
+ @Override
+ public ServiceMessage asServiceMessage() {
+ return this;
+ }
+
+ /**
+ * Returns the header of the given message. This will throw a {@link DeserializationException}
+ * if the start of the message is not a valid header.
+ */
+ public MessageHeader getHeader() {
+ return mHeader;
+ }
+
+ /**
+ * Returns the payload of the message.
+ */
+ public Message getPayload() {
+ if (mPayload == null) {
+ ByteBuffer truncatedBuffer =
+ ((ByteBuffer) getData().position(getHeader().getSize())).slice();
+ truncatedBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ mPayload = new Message(truncatedBuffer, getHandles());
+ }
+ return mPayload;
+ }
+
+ /**
+ * Set the request identifier on the message.
+ */
+ void setRequestId(long requestId) {
+ mHeader.setRequestId(getData(), requestId);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
new file mode 100644
index 0000000000..118c991f7b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
@@ -0,0 +1,21 @@
+// 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 java.io.Closeable;
+
+/**
+ * An implementation of closeable that doesn't do anything.
+ */
+public class SideEffectFreeCloseable implements Closeable {
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
new file mode 100644
index 0000000000..14f4e1e726
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
@@ -0,0 +1,88 @@
+// 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.system.Core;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Base class for all mojo structs.
+ */
+public abstract class Struct {
+ /**
+ * The base size of the encoded struct.
+ */
+ private final int mEncodedBaseSize;
+
+ /**
+ * The version of the struct.
+ */
+ private final int mVersion;
+
+ /**
+ * Constructor.
+ */
+ protected Struct(int encodedBaseSize, int version) {
+ mEncodedBaseSize = encodedBaseSize;
+ mVersion = version;
+ }
+
+ /**
+ * Returns the version of the struct. It is the max version of the struct in the mojom if it has
+ * been created locally, and the version of the received struct if it has been deserialized.
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Returns the serialization of the struct. This method can close Handles.
+ *
+ * @param core the |Core| implementation used to generate handles. Only used if the data
+ * structure being encoded contains interfaces, can be |null| otherwise.
+ */
+ public Message serialize(Core core) {
+ Encoder encoder = new Encoder(core, mEncodedBaseSize);
+ encode(encoder);
+ return encoder.getMessage();
+ }
+
+ /**
+ * Similar to the method above, but returns the serialization result as |ByteBuffer|.
+ *
+ * @throws UnsupportedOperationException if the struct contains interfaces or handles.
+ * @throws SerializationException on serialization failure.
+ */
+ public ByteBuffer serialize() {
+ // If the struct contains interfaces which require a non-null |Core| instance, it will throw
+ // UnsupportedOperationException.
+ Message message = serialize(null);
+
+ if (!message.getHandles().isEmpty())
+ throw new UnsupportedOperationException("Handles are discarded.");
+
+ return message.getData();
+ }
+
+ /**
+ * Returns the serialization of the struct prepended with the given header.
+ *
+ * @param header the header to prepend to the returned message.
+ * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+ * being encoded contains interfaces, can be |null| otherwise.
+ */
+ public ServiceMessage serializeWithHeader(Core core, MessageHeader header) {
+ Encoder encoder = new Encoder(core, mEncodedBaseSize + header.getSize());
+ header.encode(encoder);
+ encode(encoder);
+ return new ServiceMessage(encoder.getMessage(), header);
+ }
+
+ /**
+ * Use the given encoder to serialize this data structure.
+ */
+ protected abstract void encode(Encoder encoder);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java
new file mode 100644
index 0000000000..90b40ea57b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java
@@ -0,0 +1,30 @@
+// 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.system.Core;
+
+/**
+ * Base class for all mojo unions.
+ */
+public abstract class Union {
+ /**
+ * Returns the serialization of the union. This method can close Handles.
+ *
+ * @param core the |Core| implementation used to generate handles. Only used if the data
+ * structure being encoded contains interfaces, can be |null| otherwise.
+ */
+ public Message serialize(Core core) {
+ Encoder encoder = new Encoder(core, BindingsHelper.UNION_SIZE);
+ encoder.claimMemory(16);
+ encode(encoder, 0);
+ return encoder.getMessage();
+ }
+
+ /**
+ * Serializes this data structure using the given encoder.
+ */
+ protected abstract void encode(Encoder encoder, int offset);
+}
diff --git a/mojo/public/java/system/README.md b/mojo/public/java/system/README.md
new file mode 100644
index 0000000000..3213e4c790
--- /dev/null
+++ b/mojo/public/java/system/README.md
@@ -0,0 +1,25 @@
+# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo Java System API
+This document is a subset of the [Mojo documentation](/mojo).
+
+[TOC]
+
+## Overview
+
+This document provides a brief guide to Java Mojo bindings usage with example
+code snippets.
+
+For a detailed API references please consult the class definitions in
+[this directory](https://cs.chromium.org/chromium/src/mojo/public/java/system/src/org/chromium/mojo/system/).
+
+*TODO: Make the contents of this document less non-existent.*
+
+## Message Pipes
+
+## Data Pipes
+
+## Shared Buffers
+
+## Native Platform Handles (File Descriptors, Windows Handles, *etc.*)
+
+## Watchers
+
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
new file mode 100644
index 0000000000..40e4be365d
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -0,0 +1,183 @@
+// 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.system;
+
+/**
+ * Core mojo interface giving access to the base operations. See |src/mojo/public/c/system/core.h|
+ * for the underlying api.
+ */
+public interface Core {
+
+ /**
+ * Used to indicate an infinite deadline (timeout).
+ */
+ public static final long DEADLINE_INFINITE = -1;
+
+ /**
+ * Signals for the wait operations on handles.
+ */
+ public static class HandleSignals extends Flags<HandleSignals> {
+ /**
+ * Constructor.
+ *
+ * @param signals the serialized signals.
+ */
+ public HandleSignals(int signals) {
+ super(signals);
+ }
+
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_READABLE = 1 << 0;
+ private static final int FLAG_WRITABLE = 1 << 1;
+ private static final int FLAG_PEER_CLOSED = 1 << 2;
+
+ /**
+ * Immutable signals.
+ */
+ public static final HandleSignals NONE = HandleSignals.none().immutable();
+ public static final HandleSignals READABLE =
+ HandleSignals.none().setReadable(true).immutable();
+ public static final HandleSignals WRITABLE =
+ HandleSignals.none().setWritable(true).immutable();
+
+ /**
+ * Change the readable bit of this signal.
+ *
+ * @param readable the new value of the readable bit.
+ * @return this.
+ */
+ public HandleSignals setReadable(boolean readable) {
+ return setFlag(FLAG_READABLE, readable);
+ }
+
+ /**
+ * Change the writable bit of this signal.
+ *
+ * @param writable the new value of the writable bit.
+ * @return this.
+ */
+ public HandleSignals setWritable(boolean writable) {
+ return setFlag(FLAG_WRITABLE, writable);
+ }
+
+ /**
+ * Change the peer closed bit of this signal.
+ *
+ * @param peerClosed the new value of the peer closed bit.
+ * @return this.
+ */
+ public HandleSignals setPeerClosed(boolean peerClosed) {
+ return setFlag(FLAG_PEER_CLOSED, peerClosed);
+ }
+
+ /**
+ * Returns a signal with no bit set.
+ */
+ public static HandleSignals none() {
+ return new HandleSignals(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Returns a platform-dependent monotonically increasing tick count representing "right now."
+ */
+ public long getTimeTicksNow();
+
+ /**
+ * Returned by wait functions to indicate the signaling state of handles.
+ */
+ public static class HandleSignalsState {
+ /**
+ * Signals that were satisfied at some time // before the call returned.
+ */
+ private final HandleSignals mSatisfiedSignals;
+
+ /**
+ * Signals that are possible to satisfy. For example, if the return value was
+ * |MOJO_RESULT_FAILED_PRECONDITION|, you can use this field to determine which, if any, of
+ * the signals can still be satisfied.
+ */
+ private final HandleSignals mSatisfiableSignals;
+
+ /**
+ * Constructor.
+ */
+ public HandleSignalsState(
+ HandleSignals satisfiedSignals, HandleSignals satisfiableSignals) {
+ mSatisfiedSignals = satisfiedSignals;
+ mSatisfiableSignals = satisfiableSignals;
+ }
+
+ /**
+ * Returns the satisfiedSignals.
+ */
+ public HandleSignals getSatisfiedSignals() {
+ return mSatisfiedSignals;
+ }
+
+ /**
+ * Returns the satisfiableSignals.
+ */
+ public HandleSignals getSatisfiableSignals() {
+ return mSatisfiableSignals;
+ }
+ }
+
+ /**
+ * Creates a message pipe, which is a bidirectional communication channel for framed data (i.e.,
+ * messages), with the given options. Messages can contain plain data and/or Mojo handles.
+ *
+ * @return the set of handles for the two endpoints (ports) of the message pipe.
+ */
+ public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe(
+ MessagePipeHandle.CreateOptions options);
+
+ /**
+ * Creates a data pipe, which is a unidirectional communication channel for unframed data, with
+ * the given options. Data is unframed, but must come as (multiples of) discrete elements, of
+ * the size given in |options|. See |DataPipe.CreateOptions| for a description of the different
+ * options available for data pipes. |options| may be set to null for a data pipe with the
+ * default options (which will have an element size of one byte and have some system-dependent
+ * capacity).
+ *
+ * @return the set of handles for the two endpoints of the data pipe.
+ */
+ public Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> createDataPipe(
+ DataPipe.CreateOptions options);
+
+ /**
+ * Creates a buffer that can be shared between applications (by duplicating the handle -- see
+ * |SharedBufferHandle.duplicate()| -- and passing it over a message pipe). To access the
+ * buffer, one must call |SharedBufferHandle.map|.
+ *
+ * @return the new |SharedBufferHandle|.
+ */
+ public SharedBufferHandle createSharedBuffer(SharedBufferHandle.CreateOptions options,
+ long numBytes);
+
+ /**
+ * Acquires a handle from the native side. The handle will be owned by the returned object and
+ * must not be closed outside of it.
+ *
+ * @return a new {@link UntypedHandle} representing the native handle.
+ */
+ public UntypedHandle acquireNativeHandle(int handle);
+
+ /**
+ * Returns an implementation of {@link Watcher}.
+ */
+ public Watcher getWatcher();
+
+ /**
+ * Returns a new run loop.
+ */
+ public RunLoop createDefaultRunLoop();
+
+ /**
+ * Returns the current run loop if it exists.
+ */
+ public RunLoop getCurrentRunLoop();
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
new file mode 100644
index 0000000000..4deaf0975d
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
@@ -0,0 +1,334 @@
+// 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.system;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Interface for data pipes. A data pipe is a unidirectional communication channel for unframed
+ * data. Data is unframed, but must come as (multiples of) discrete elements, of the size given at
+ * creation time.
+ */
+public interface DataPipe {
+
+ /**
+ * Flags for the data pipe creation operation.
+ */
+ public static class CreateFlags extends Flags<CreateFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected CreateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static CreateFlags none() {
+ return new CreateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify creation parameters for a data pipe to |Core.createDataPipe()|.
+ */
+ public static class CreateOptions {
+
+ /**
+ * Used to specify different modes of operation, see |DataPipe.CreateFlags|.
+ */
+ private CreateFlags mFlags = CreateFlags.none();
+ /**
+ * The size of an element, in bytes. All transactions and buffers will consist of an
+ * integral number of elements. Must be nonzero.
+ */
+ private int mElementNumBytes;
+ /**
+ * The capacity of the data pipe, in number of bytes; must be a multiple of
+ * |element_num_bytes|. The data pipe will always be able to queue AT LEAST this much data.
+ * Set to zero to opt for a system-dependent automatically-calculated capacity (which will
+ * always be at least one element).
+ */
+ private int mCapacityNumBytes;
+
+ /**
+ * @return the flags
+ */
+ public CreateFlags getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * @return the elementNumBytes
+ */
+ public int getElementNumBytes() {
+ return mElementNumBytes;
+ }
+
+ /**
+ * @param elementNumBytes the elementNumBytes to set
+ */
+ public void setElementNumBytes(int elementNumBytes) {
+ mElementNumBytes = elementNumBytes;
+ }
+
+ /**
+ * @return the capacityNumBytes
+ */
+ public int getCapacityNumBytes() {
+ return mCapacityNumBytes;
+ }
+
+ /**
+ * @param capacityNumBytes the capacityNumBytes to set
+ */
+ public void setCapacityNumBytes(int capacityNumBytes) {
+ mCapacityNumBytes = capacityNumBytes;
+ }
+
+ }
+
+ /**
+ * Flags for the write operations on MessagePipeHandle .
+ */
+ public static class WriteFlags extends Flags<WriteFlags> {
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_ALL_OR_NONE = 1 << 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final WriteFlags NONE = WriteFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ private WriteFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Change the all-or-none bit of those flags. If set, write either all the elements
+ * requested or none of them.
+ *
+ * @param allOrNone the new value of all-or-none bit.
+ * @return this.
+ */
+ public WriteFlags setAllOrNone(boolean allOrNone) {
+ return setFlag(FLAG_ALL_OR_NONE, allOrNone);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static WriteFlags none() {
+ return new WriteFlags(FLAG_NONE);
+ }
+ }
+
+ /**
+ * Flags for the read operations on MessagePipeHandle.
+ */
+ public static class ReadFlags extends Flags<ReadFlags> {
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_ALL_OR_NONE = 1 << 0;
+ private static final int FLAG_QUERY = 1 << 2;
+ private static final int FLAG_PEEK = 1 << 3;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final ReadFlags NONE = ReadFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ private ReadFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Change the all-or-none bit of this flag. If set, read (or discard) either the requested
+ * number of elements or none.
+ *
+ * @param allOrNone the new value of the all-or-none bit.
+ * @return this.
+ */
+ public ReadFlags setAllOrNone(boolean allOrNone) {
+ return setFlag(FLAG_ALL_OR_NONE, allOrNone);
+ }
+
+ /**
+ * Change the query bit of this flag. If set query the number of elements available to read.
+ * Mutually exclusive with |discard| and |allOrNone| is ignored if this flag is set.
+ *
+ * @param query the new value of the query bit.
+ * @return this.
+ */
+ public ReadFlags query(boolean query) {
+ return setFlag(FLAG_QUERY, query);
+ }
+
+ /**
+ * Change the peek bit of this flag. If set, read the requested number of elements, and
+ * leave those elements in the pipe. A later read will return the same data.
+ * Mutually exclusive with |discard| and |query|.
+ *
+ * @param peek the new value of the peek bit.
+ * @return this.
+ */
+ public ReadFlags peek(boolean peek) {
+ return setFlag(FLAG_PEEK, peek);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static ReadFlags none() {
+ return new ReadFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Handle for the producer part of a data pipe.
+ */
+ public static interface ProducerHandle extends Handle {
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public ProducerHandle pass();
+
+ /**
+ * Writes the given data to the data pipe producer. |elements| points to data; the buffer
+ * must be a direct ByteBuffer and the limit should be a multiple of the data pipe's element
+ * size. If |allOrNone| is set in |flags|, either all the data will be written or none is.
+ * <p>
+ * On success, returns the amount of data that was actually written.
+ * <p>
+ * Note: If the data pipe has the "may discard" option flag (specified on creation), this
+ * will discard as much data as required to write the given data, starting with the earliest
+ * written data that has not been consumed. However, even with "may discard", if the buffer
+ * limit is greater than the data pipe's capacity (and |allOrNone| is not set), this will
+ * write the maximum amount possible (namely, the data pipe's capacity) and return that
+ * amount. It will *not* discard data from |elements|.
+ *
+ * @return number of written bytes.
+ */
+ public ResultAnd<Integer> writeData(ByteBuffer elements, WriteFlags flags);
+
+ /**
+ * Begins a two-phase write to the data pipe producer . On success, returns a |ByteBuffer|
+ * to which the caller can write. If flags has |allOrNone| set, then the buffer capacity
+ * will be at least as large as |numBytes|, which must also be a multiple of the element
+ * size (if |allOrNone| is not set, |numBytes| is ignored and the caller must check the
+ * capacity of the buffer).
+ * <p>
+ * During a two-phase write, this handle is *not* writable. E.g., if another thread tries to
+ * write to it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread can
+ * then wait for this handle to become writable again.
+ * <p>
+ * Once the caller has finished writing data to the buffer, it should call |endWriteData()|
+ * to specify the amount written and to complete the two-phase write.
+ * <p>
+ * Note: If the data pipe has the "may discard" option flag (specified on creation) and
+ * |flags| has |allOrNone| set, this may discard some data.
+ *
+ * @return The buffer to write to.
+ */
+ public ByteBuffer beginWriteData(int numBytes, WriteFlags flags);
+
+ /**
+ * Ends a two-phase write to the data pipe producer that was begun by a call to
+ * |beginWriteData()| on the same handle. |numBytesWritten| should indicate the amount of
+ * data actually written; it must be less than or equal to the capacity of the buffer
+ * returned by |beginWriteData()| and must be a multiple of the element size. The buffer
+ * returned from |beginWriteData()| must have been filled with exactly |numBytesWritten|
+ * bytes of data.
+ * <p>
+ * On failure, the two-phase write (if any) is ended (so the handle may become writable
+ * again, if there's space available) but no data written to the buffer is "put into" the
+ * data pipe.
+ */
+ public void endWriteData(int numBytesWritten);
+ }
+
+ /**
+ * Handle for the consumer part of a data pipe.
+ */
+ public static interface ConsumerHandle extends Handle {
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public ConsumerHandle pass();
+
+ /**
+ * Discards data on the data pie consumer. This method discards up to |numBytes| (which
+ * again be a multiple of the element size) bytes of data, returning the amount actually
+ * discarded. if |flags| has |allOrNone|, it will either discard exactly |numBytes| bytes of
+ * data or none. In this case, |query| must not be set.
+ */
+ public int discardData(int numBytes, ReadFlags flags);
+
+ /**
+ * Reads data from the data pipe consumer. May also be used to query the amount of data
+ * available. If |flags| has not |query| set, this tries to read up to |elements| capacity
+ * (which must be a multiple of the data pipe's element size) bytes of data to |elements|
+ * and returns the amount actually read. |elements| must be a direct ByteBuffer. If flags
+ * has |allOrNone| set, it will either read exactly |elements| capacity bytes of data or
+ * none.
+ * <p>
+ * If flags has |query| set, it queries the amount of data available, returning the number
+ * of bytes available. In this case |allOrNone| is ignored, as are |elements|.
+ */
+ public ResultAnd<Integer> readData(ByteBuffer elements, ReadFlags flags);
+
+ /**
+ * Begins a two-phase read from the data pipe consumer. On success, returns a |ByteBuffer|
+ * from which the caller can read up to its limit bytes of data. If flags has |allOrNone|
+ * set, then the limit will be at least as large as |numBytes|, which must also be a
+ * multiple of the element size (if |allOrNone| is not set, |numBytes| is ignored). |flags|
+ * must not have |query| set.
+ * <p>
+ * During a two-phase read, this handle is *not* readable. E.g., if another thread tries to
+ * read from it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread
+ * can then wait for this handle to become readable again.
+ * <p>
+ * Once the caller has finished reading data from the buffer, it should call |endReadData()|
+ * to specify the amount read and to complete the two-phase read.
+ */
+ public ByteBuffer beginReadData(int numBytes, ReadFlags flags);
+
+ /**
+ * Ends a two-phase read from the data pipe consumer that was begun by a call to
+ * |beginReadData()| on the same handle. |numBytesRead| should indicate the amount of data
+ * actually read; it must be less than or equal to the limit of the buffer returned by
+ * |beginReadData()| and must be a multiple of the element size.
+ * <p>
+ * On failure, the two-phase read (if any) is ended (so the handle may become readable
+ * again) but no data is "removed" from the data pipe.
+ */
+ public void endReadData(int numBytesRead);
+ }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
new file mode 100644
index 0000000000..30ff07f710
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
@@ -0,0 +1,83 @@
+// 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.system;
+
+/**
+ * Base class for bit field used as flags.
+ *
+ * @param <F> the type of the flags.
+ */
+public abstract class Flags<F extends Flags<F>> {
+ private int mFlags;
+ private boolean mImmutable;
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ protected Flags(int flags) {
+ mImmutable = false;
+ mFlags = flags;
+ }
+
+ /**
+ * @return the computed flag.
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Change the given bit of this flag.
+ *
+ * @param value the new value of given bit.
+ * @return this.
+ */
+ protected F setFlag(int flag, boolean value) {
+ if (mImmutable) {
+ throw new UnsupportedOperationException("Flags is immutable.");
+ }
+ if (value) {
+ mFlags |= flag;
+ } else {
+ mFlags &= ~flag;
+ }
+ @SuppressWarnings("unchecked")
+ F f = (F) this;
+ return f;
+ }
+
+ /**
+ * Makes this flag immutable. This is a non-reversable operation.
+ */
+ protected F immutable() {
+ mImmutable = true;
+ @SuppressWarnings("unchecked")
+ F f = (F) this;
+ return f;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return mFlags;
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Flags<?> other = (Flags<?>) obj;
+ if (mFlags != other.mFlags) return false;
+ return true;
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
new file mode 100644
index 0000000000..903f36d677
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
@@ -0,0 +1,61 @@
+// 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.system;
+
+import org.chromium.mojo.system.Core.HandleSignalsState;
+
+import java.io.Closeable;
+
+/**
+ * A generic mojo handle.
+ */
+public interface Handle extends Closeable {
+
+ /**
+ * Closes the given |handle|.
+ * <p>
+ * Concurrent operations on |handle| may succeed (or fail as usual) if they happen before the
+ * close, be cancelled with result |MojoResult.CANCELLED| if they properly overlap (this is
+ * likely the case with |wait()|, etc.), or fail with |MojoResult.INVALID_ARGUMENT| if they
+ * happen after.
+ */
+ @Override
+ public void close();
+
+ /**
+ * @return the last known signaling state of the handle.
+ */
+ public HandleSignalsState querySignalsState();
+
+ /**
+ * @return whether the handle is valid. A handle is valid until it has been explicitly closed or
+ * send through a message pipe via |MessagePipeHandle.writeMessage|.
+ */
+ public boolean isValid();
+
+ /**
+ * Converts this handle into an {@link UntypedHandle}, invalidating this handle.
+ */
+ public UntypedHandle toUntypedHandle();
+
+ /**
+ * Returns the {@link Core} implementation for this handle. Can be null if this handle is
+ * invalid.
+ */
+ public Core getCore();
+
+ /**
+ * Passes ownership of the handle from this handle to the newly created Handle object,
+ * invalidating this handle object in the process.
+ */
+ public Handle pass();
+
+ /**
+ * Releases the native handle backed by this {@link Handle}. The caller owns the handle and must
+ * close it.
+ */
+ public int releaseNativeHandle();
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
new file mode 100644
index 0000000000..f8b99c6d66
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
@@ -0,0 +1,219 @@
+// 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.system;
+
+import org.chromium.mojo.system.Core.HandleSignalsState;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A handle that will always be invalid.
+ */
+public class InvalidHandle implements UntypedHandle, MessagePipeHandle, ConsumerHandle,
+ ProducerHandle, SharedBufferHandle {
+
+ /**
+ * Instance singleton.
+ */
+ public static final InvalidHandle INSTANCE = new InvalidHandle();
+
+ /**
+ * Private constructor.
+ */
+ private InvalidHandle() {
+ }
+
+ /**
+ * @see Handle#close()
+ */
+ @Override
+ public void close() {
+ // Do nothing.
+ }
+
+ /**
+ * @see Handle#querySignalsState()
+ */
+ @Override
+ public HandleSignalsState querySignalsState() {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see Handle#isValid()
+ */
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+
+ /**
+ * @see Handle#getCore()
+ */
+ @Override
+ public Core getCore() {
+ return null;
+ }
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public InvalidHandle pass() {
+ return this;
+ }
+
+ /**
+ * @see Handle#toUntypedHandle()
+ */
+ @Override
+ public UntypedHandle toUntypedHandle() {
+ return this;
+ }
+
+ /**
+ * @see Handle#releaseNativeHandle()
+ */
+ @Override
+ public int releaseNativeHandle() {
+ return 0;
+ }
+
+ /**
+ * @see UntypedHandle#toMessagePipeHandle()
+ */
+ @Override
+ public MessagePipeHandle toMessagePipeHandle() {
+ return this;
+ }
+
+ /**
+ * @see UntypedHandle#toDataPipeConsumerHandle()
+ */
+ @Override
+ public ConsumerHandle toDataPipeConsumerHandle() {
+ return this;
+ }
+
+ /**
+ * @see UntypedHandle#toDataPipeProducerHandle()
+ */
+ @Override
+ public ProducerHandle toDataPipeProducerHandle() {
+ return this;
+ }
+
+ /**
+ * @see UntypedHandle#toSharedBufferHandle()
+ */
+ @Override
+ public SharedBufferHandle toSharedBufferHandle() {
+ return this;
+ }
+
+ /**
+ * @see SharedBufferHandle#duplicate(SharedBufferHandle.DuplicateOptions)
+ */
+ @Override
+ public SharedBufferHandle duplicate(DuplicateOptions options) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see SharedBufferHandle#map(long, long, SharedBufferHandle.MapFlags)
+ */
+ @Override
+ public ByteBuffer map(long offset, long numBytes, MapFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see SharedBufferHandle#unmap(java.nio.ByteBuffer)
+ */
+ @Override
+ public void unmap(ByteBuffer buffer) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ProducerHandle#writeData(java.nio.ByteBuffer, DataPipe.WriteFlags)
+ */
+ @Override
+ public ResultAnd<Integer> writeData(ByteBuffer elements, DataPipe.WriteFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ProducerHandle#beginWriteData(int, DataPipe.WriteFlags)
+ */
+ @Override
+ public ByteBuffer beginWriteData(int numBytes,
+ DataPipe.WriteFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ProducerHandle#endWriteData(int)
+ */
+ @Override
+ public void endWriteData(int numBytesWritten) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#discardData(int, DataPipe.ReadFlags)
+ */
+ @Override
+ public int discardData(int numBytes, DataPipe.ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#readData(java.nio.ByteBuffer, DataPipe.ReadFlags)
+ */
+ @Override
+ public ResultAnd<Integer> readData(ByteBuffer elements, DataPipe.ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#beginReadData(int, DataPipe.ReadFlags)
+ */
+ @Override
+ public ByteBuffer beginReadData(int numBytes,
+ DataPipe.ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#endReadData(int)
+ */
+ @Override
+ public void endReadData(int numBytesRead) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see MessagePipeHandle#writeMessage(java.nio.ByteBuffer, java.util.List,
+ * MessagePipeHandle.WriteFlags)
+ */
+ @Override
+ public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see MessagePipeHandle#readMessage(java.nio.ByteBuffer, int, MessagePipeHandle.ReadFlags)
+ */
+ @Override
+ public ResultAnd<ReadMessageResult> readMessage(
+ ByteBuffer bytes, int maxNumberOfHandles, ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
new file mode 100644
index 0000000000..deb6ac0f01
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
@@ -0,0 +1,224 @@
+// 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.system;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Message pipes are bidirectional communication channel for framed data (i.e., messages). Messages
+ * can contain plain data and/or Mojo handles.
+ */
+public interface MessagePipeHandle extends Handle {
+
+ /**
+ * Flags for the message pipe creation operation.
+ */
+ public static class CreateFlags extends Flags<CreateFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected CreateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static CreateFlags none() {
+ return new CreateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify creation parameters for a message pipe to |Core#createMessagePipe()|.
+ */
+ public static class CreateOptions {
+ private CreateFlags mFlags = CreateFlags.NONE;
+
+ /**
+ * @return the flags
+ */
+ public CreateFlags getFlags() {
+ return mFlags;
+ }
+
+ }
+
+ /**
+ * Flags for the write operations on MessagePipeHandle .
+ */
+ public static class WriteFlags extends Flags<WriteFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with no bit set.
+ */
+ public static final WriteFlags NONE = WriteFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ private WriteFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static WriteFlags none() {
+ return new WriteFlags(FLAG_NONE);
+ }
+ }
+
+ /**
+ * Flags for the read operations on MessagePipeHandle.
+ */
+ public static class ReadFlags extends Flags<ReadFlags> {
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_MAY_DISCARD = 1 << 0;
+
+ /**
+ * Immutable flag with no bit set.
+ */
+ public static final ReadFlags NONE = ReadFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ private ReadFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Change the may-discard bit of this flag. If set, if the message is unable to be read for
+ * whatever reason (e.g., the caller-supplied buffer is too small), discard the message
+ * (i.e., simply dequeue it).
+ *
+ * @param mayDiscard the new value of the may-discard bit.
+ * @return this.
+ */
+ public ReadFlags setMayDiscard(boolean mayDiscard) {
+ return setFlag(FLAG_MAY_DISCARD, mayDiscard);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static ReadFlags none() {
+ return new ReadFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Result of the |readMessage| method.
+ */
+ public static class ReadMessageResult {
+ /**
+ * If a message was read, the size in bytes of the message, otherwise the size in bytes of
+ * the next message.
+ */
+ private int mMessageSize;
+ /**
+ * If a message was read, the number of handles contained in the message, otherwise the
+ * number of handles contained in the next message.
+ */
+ private int mHandlesCount;
+ /**
+ * If a message was read, the handles contained in the message, undefined otherwise.
+ */
+ private List<UntypedHandle> mHandles;
+
+ /**
+ * @return the messageSize
+ */
+ public int getMessageSize() {
+ return mMessageSize;
+ }
+
+ /**
+ * @param messageSize the messageSize to set
+ */
+ public void setMessageSize(int messageSize) {
+ mMessageSize = messageSize;
+ }
+
+ /**
+ * @return the handlesCount
+ */
+ public int getHandlesCount() {
+ return mHandlesCount;
+ }
+
+ /**
+ * @param handlesCount the handlesCount to set
+ */
+ public void setHandlesCount(int handlesCount) {
+ mHandlesCount = handlesCount;
+ }
+
+ /**
+ * @return the handles
+ */
+ public List<UntypedHandle> getHandles() {
+ return mHandles;
+ }
+
+ /**
+ * @param handles the handles to set
+ */
+ public void setHandles(List<UntypedHandle> handles) {
+ mHandles = handles;
+ }
+ }
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public MessagePipeHandle pass();
+
+ /**
+ * Writes a message to the message pipe endpoint, with message data specified by |bytes| and
+ * attached handles specified by |handles|, and options specified by |flags|. If there is no
+ * message data, |bytes| may be null, otherwise it must be a direct ByteBuffer. If there are no
+ * attached handles, |handles| may be null.
+ * <p>
+ * If handles are attached, on success the handles will no longer be valid (the receiver will
+ * receive equivalent, but logically different, handles). Handles to be sent should not be in
+ * simultaneous use (e.g., on another thread).
+ */
+ void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags);
+
+ /**
+ * Reads a message from the message pipe endpoint; also usable to query the size of the next
+ * message or discard the next message. |bytes| indicate the buffer/buffer size to receive the
+ * message data (if any) and |maxNumberOfHandles| indicate the maximum handle count to receive
+ * the attached handles (if any). |bytes| is optional. If null, |maxNumberOfHandles| must be
+ * zero, and the return value will indicate the size of the next message. If non-null, it must
+ * be a direct ByteBuffer and the return value will indicate if the message was read or not. If
+ * the message was read its content will be in |bytes|, and the attached handles will be in the
+ * return value. Partial reads are NEVER done. Either a full read is done and |wasMessageRead|
+ * will be true, or the read is NOT done and |wasMessageRead| will be false (if |mayDiscard| was
+ * set, the message is also discarded in this case).
+ */
+ ResultAnd<ReadMessageResult> readMessage(
+ ByteBuffer bytes, int maxNumberOfHandles, ReadFlags flags);
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
new file mode 100644
index 0000000000..4e0e3e9597
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
@@ -0,0 +1,44 @@
+// 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.system;
+
+/**
+ * Exception for the core mojo API.
+ */
+public class MojoException extends RuntimeException {
+
+ private final int mCode;
+
+ /**
+ * Constructor.
+ */
+ public MojoException(int code) {
+ mCode = code;
+ }
+
+ /**
+ * Constructor.
+ */
+ public MojoException(Throwable cause) {
+ super(cause);
+ mCode = MojoResult.UNKNOWN;
+ }
+
+ /**
+ * The mojo result code associated with this exception. See {@link MojoResult} for possible
+ * values.
+ */
+ public int getMojoResult() {
+ return mCode;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "MojoResult(" + mCode + "): " + MojoResult.describe(mCode);
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
new file mode 100644
index 0000000000..2602cb5e1e
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
@@ -0,0 +1,82 @@
+// 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.system;
+
+/**
+ * The different mojo result codes.
+ */
+public final class MojoResult {
+ public static final int OK = 0;
+ public static final int CANCELLED = 1;
+ public static final int UNKNOWN = 2;
+ public static final int INVALID_ARGUMENT = 3;
+ public static final int DEADLINE_EXCEEDED = 4;
+ public static final int NOT_FOUND = 5;
+ public static final int ALREADY_EXISTS = 6;
+ public static final int PERMISSION_DENIED = 7;
+ public static final int RESOURCE_EXHAUSTED = 8;
+ public static final int FAILED_PRECONDITION = 9;
+ public static final int ABORTED = 10;
+ public static final int OUT_OF_RANGE = 11;
+ public static final int UNIMPLEMENTED = 12;
+ public static final int INTERNAL = 13;
+ public static final int UNAVAILABLE = 14;
+ public static final int DATA_LOSS = 15;
+ public static final int BUSY = 16;
+ public static final int SHOULD_WAIT = 17;
+
+ /**
+ * never instantiate.
+ */
+ private MojoResult() {
+ }
+
+ /**
+ * Describes the given result code.
+ */
+ public static String describe(int mCode) {
+ switch (mCode) {
+ case OK:
+ return "OK";
+ case CANCELLED:
+ return "CANCELLED";
+ case UNKNOWN:
+ return "UNKNOWN";
+ case INVALID_ARGUMENT:
+ return "INVALID_ARGUMENT";
+ case DEADLINE_EXCEEDED:
+ return "DEADLINE_EXCEEDED";
+ case NOT_FOUND:
+ return "NOT_FOUND";
+ case ALREADY_EXISTS:
+ return "ALREADY_EXISTS";
+ case PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case RESOURCE_EXHAUSTED:
+ return "RESOURCE_EXHAUSTED";
+ case FAILED_PRECONDITION:
+ return "FAILED_PRECONDITION";
+ case ABORTED:
+ return "ABORTED";
+ case OUT_OF_RANGE:
+ return "OUT_OF_RANGE";
+ case UNIMPLEMENTED:
+ return "UNIMPLEMENTED";
+ case INTERNAL:
+ return "INTERNAL";
+ case UNAVAILABLE:
+ return "UNAVAILABLE";
+ case DATA_LOSS:
+ return "DATA_LOSS";
+ case BUSY:
+ return "BUSY";
+ case SHOULD_WAIT:
+ return "SHOULD_WAIT";
+ default:
+ return "UNKNOWN";
+ }
+
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
new file mode 100644
index 0000000000..2ead0204f9
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
@@ -0,0 +1,67 @@
+// 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.system;
+
+
+/**
+ * A pair of object.
+ *
+ * @param <F> Type of the first element.
+ * @param <S> Type of the second element.
+ */
+public class Pair<F, S> {
+
+ public final F first;
+ public final S second;
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param first the first element of the pair.
+ * @param second the second element of the pair.
+ */
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * equals() that handles null values.
+ */
+ private boolean equals(Object o1, Object o2) {
+ return o1 == null ? o2 == null : o1.equals(o2);
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Pair)) {
+ return false;
+ }
+ Pair<?, ?> p = (Pair<?, ?>) o;
+ return equals(first, p.first) && equals(second, p.second);
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+ }
+
+ /**
+ * Helper method for creating a pair.
+ *
+ * @param a the first element of the pair.
+ * @param b the second element of the pair.
+ * @return the pair containing a and b.
+ */
+ public static <A, B> Pair<A, B> create(A a, B b) {
+ return new Pair<A, B>(a, b);
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java
new file mode 100644
index 0000000000..656d0d64e4
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java
@@ -0,0 +1,34 @@
+// 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.system;
+
+/**
+ * Container that contains a mojo result and a value.
+ *
+ * @param <A> the type of the value.
+ */
+public class ResultAnd<A> {
+ private final int mMojoResult;
+ private final A mValue;
+
+ public ResultAnd(int result, A value) {
+ this.mMojoResult = result;
+ this.mValue = value;
+ }
+
+ /**
+ * Returns the mojo result.
+ */
+ public int getMojoResult() {
+ return mMojoResult;
+ }
+
+ /**
+ * Returns the value.
+ */
+ public A getValue() {
+ return mValue;
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java
new file mode 100644
index 0000000000..4038b2954e
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java
@@ -0,0 +1,41 @@
+// 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.system;
+
+import java.io.Closeable;
+
+/**
+ * Definition of a run loop.
+ */
+public interface RunLoop extends Closeable {
+ /**
+ * Start the run loop. It will continue until quit() is called.
+ */
+ public void run();
+
+ /**
+ * Start the run loop and stop it as soon as no task is present in the work queue.
+ */
+ public void runUntilIdle();
+
+ /*
+ * Quit the currently running run loop.
+ */
+ public void quit();
+
+ /**
+ * Add a runnable to the queue of tasks.
+ * @param runnable Callback to be executed by the run loop.
+ * @param delay Delay, in MojoTimeTicks (microseconds) before the callback should
+ * be executed.
+ */
+ public void postDelayedTask(Runnable runnable, long delay);
+
+ /**
+ * Destroy the run loop and deregister it from Core.
+ */
+ @Override
+ public abstract void close();
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
new file mode 100644
index 0000000000..df317d134d
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
@@ -0,0 +1,160 @@
+// 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.system;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A buffer that can be shared between applications.
+ */
+public interface SharedBufferHandle extends Handle {
+
+ /**
+ * Flags for the shared buffer creation operation.
+ */
+ public static class CreateFlags extends Flags<CreateFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected CreateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static CreateFlags none() {
+ return new CreateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify creation parameters for a shared buffer to |Core#createSharedBuffer()|.
+ */
+ public static class CreateOptions {
+ private CreateFlags mFlags = CreateFlags.NONE;
+
+ /**
+ * @return the flags
+ */
+ public CreateFlags getFlags() {
+ return mFlags;
+ }
+
+ }
+
+ /**
+ * Flags for the shared buffer duplication operation.
+ */
+ public static class DuplicateFlags extends Flags<DuplicateFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final DuplicateFlags NONE = DuplicateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected DuplicateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static DuplicateFlags none() {
+ return new DuplicateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify parameters in duplicating access to a shared buffer to
+ * |SharedBufferHandle#duplicate|
+ */
+ public static class DuplicateOptions {
+ private DuplicateFlags mFlags = DuplicateFlags.NONE;
+
+ /**
+ * @return the flags
+ */
+ public DuplicateFlags getFlags() {
+ return mFlags;
+ }
+
+ }
+
+ /**
+ * Flags for the shared buffer map operation.
+ */
+ public static class MapFlags extends Flags<MapFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final MapFlags NONE = MapFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected MapFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static MapFlags none() {
+ return new MapFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public SharedBufferHandle pass();
+
+ /**
+ * Duplicates the handle. This creates another handle (returned on success), which can then be
+ * sent to another application over a message pipe, while retaining access to this handle (and
+ * any mappings that it may have).
+ */
+ public SharedBufferHandle duplicate(DuplicateOptions options);
+
+ /**
+ * Map the part (at offset |offset| of length |numBytes|) of the buffer given by this handle
+ * into memory. |offset + numBytes| must be less than or equal to the size of the buffer. On
+ * success, the returned buffer points to memory with the requested part of the buffer. A single
+ * buffer handle may have multiple active mappings (possibly depending on the buffer type). The
+ * permissions (e.g., writable or executable) of the returned memory may depend on the
+ * properties of the buffer and properties attached to the buffer handle as well as |flags|.
+ */
+ public ByteBuffer map(long offset, long numBytes, MapFlags flags);
+
+ /**
+ * Unmap a buffer pointer that was mapped by |map()|.
+ */
+ public void unmap(ByteBuffer buffer);
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
new file mode 100644
index 0000000000..199b0a1c04
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
@@ -0,0 +1,45 @@
+// 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.system;
+
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+
+/**
+ * A mojo handle of unknown type. This handle can be typed by using one of its methods, which will
+ * return a handle of the requested type and invalidate this object. No validation is made when the
+ * conversion operation is called.
+ */
+public interface UntypedHandle extends Handle {
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public UntypedHandle pass();
+
+ /**
+ * Returns the underlying handle, as a {@link MessagePipeHandle}, invalidating this
+ * representation.
+ */
+ public MessagePipeHandle toMessagePipeHandle();
+
+ /**
+ * Returns the underlying handle, as a {@link ConsumerHandle}, invalidating this representation.
+ */
+ public ConsumerHandle toDataPipeConsumerHandle();
+
+ /**
+ * Returns the underlying handle, as a {@link ProducerHandle}, invalidating this representation.
+ */
+ public ProducerHandle toDataPipeProducerHandle();
+
+ /**
+ * Returns the underlying handle, as a {@link SharedBufferHandle}, invalidating this
+ * representation.
+ */
+ public SharedBufferHandle toSharedBufferHandle();
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java b/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
new file mode 100644
index 0000000000..9c70161067
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
@@ -0,0 +1,38 @@
+// Copyright 2016 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.system;
+
+import org.chromium.mojo.system.Core.HandleSignals;
+
+/**
+ * Watches a handle for signals being satisfied.
+ */
+public interface Watcher {
+ /**
+ * Callback passed to {@link Watcher#start}.
+ */
+ public interface Callback {
+ /**
+ * Called when the handle is ready.
+ */
+ public void onResult(int result);
+ }
+
+ /**
+ * Starts watching a handle.
+ */
+ int start(Handle handle, HandleSignals signals, Callback callback);
+
+ /**
+ * Cancels an already-started watch.
+ */
+ void cancel();
+
+ /**
+ * Destroys the underlying implementation. Other methods will fail after destroy has been
+ * called.
+ */
+ void destroy();
+}