[mojo]: Add a way to handle unhandled RuntimeExceptions
am: 1fef1a6783

Change-Id: I7674a6651149e599af532b396b53a5722fc603bc
diff --git a/libchrome_tools/patch/mojo-Add-a-way-to-handle-unhandled-RuntimeExceptions.patch b/libchrome_tools/patch/mojo-Add-a-way-to-handle-unhandled-RuntimeExceptions.patch
new file mode 100644
index 0000000..f5ac205
--- /dev/null
+++ b/libchrome_tools/patch/mojo-Add-a-way-to-handle-unhandled-RuntimeExceptions.patch
@@ -0,0 +1,129 @@
+From 4256ecec730fdf5a41f34e11c0641e072971cb8c Mon Sep 17 00:00:00 2001
+From: Luis Hector Chavez <lhchavez@google.com>
+Date: Mon, 18 Jun 2018 20:14:56 +0000
+Subject: [PATCH] [mojo] Add a way to handle unhandled RuntimeExceptions
+
+This change makes it possible to allow interfaces to globally handle
+unhandled RuntimeExceptions, in their bindings or in the callbacks.
+
+      delegate can now forward the unhandled exceptions to the crash
+      server.
+
+Bug: 810087
+Test: Android-on-Chrome OS has the same behavior as before
+Test: Android-on-Chrome OS, when setting the DefaultExceptionHandler's
+Change-Id: I2b7455a0344a109e1d2416a74ad4a0b98cd007f0
+Reviewed-on: https://chromium-review.googlesource.com/1101898
+Reviewed-by: Ken Rockot <rockot@chromium.org>
+Commit-Queue: Luis Hector Chavez <lhchavez@chromium.org>
+Cr-Commit-Position: refs/heads/master@{#568128}
+---
+ mojo/public/java/BUILD.gn                     |  1 +
+ .../org/chromium/mojo/bindings/Connector.java | 12 +++-
+ .../mojo/bindings/ExceptionHandler.java       | 59 +++++++++++++++++++
+ 3 files changed, 70 insertions(+), 2 deletions(-)
+ create mode 100644 mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
+
+diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
+index 14951f4f0959..259e09cf7c07 100644
+--- a/mojo/public/java/BUILD.gn
++++ b/mojo/public/java/BUILD.gn
+@@ -41,6 +41,7 @@ android_library("bindings_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/ExceptionHandler.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",
+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
+index 3a6d67112ce0..45f1fc7462e8 100644
+--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
++++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
+@@ -201,8 +201,16 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle
+         ReadMessageResult readResult = result.getValue();
+         assert readResult != null;
+         if (receiver != null) {
+-            boolean accepted = receiver.accept(
+-                    new Message(ByteBuffer.wrap(readResult.mData), readResult.mHandles));
++            boolean accepted;
++            try {
++                accepted = receiver.accept(
++                        new Message(ByteBuffer.wrap(readResult.mData), readResult.mHandles));
++            } catch (RuntimeException e) {
++                // The DefaultExceptionHandler will decide whether any uncaught exception will
++                // close the connection or not.
++                accepted =
++                        ExceptionHandler.DefaultExceptionHandler.getInstance().handleException(e);
++            }
+             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/ExceptionHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
+new file mode 100644
+index 000000000000..8961d22d3ee4
+--- /dev/null
++++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
+@@ -0,0 +1,59 @@
++// Copyright 2018 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;
++
++/**
++ * An {@link ExceptionHandler} is notified of any {@link RuntimeException} happening in the
++ * bindings or any of the callbacks.
++ */
++public interface ExceptionHandler {
++    /**
++     * Receives a notification that an unhandled {@link RuntimeException} has been thrown in an
++     * {@link Interface} implementation or one of the {@link Callbacks} internal classes.
++     *
++     * Normal implementations should either throw the exception or return whether the connection
++     * should be kept alive or terminated.
++     */
++    public boolean handleException(RuntimeException e);
++
++    /**
++     * The default ExceptionHandler, which simply throws the exception upon receiving it. It can
++     * also delegate the handling of the exceptions to another instance of ExceptionHandler.
++     */
++    public static class DefaultExceptionHandler implements ExceptionHandler {
++        private ExceptionHandler mDelegate;
++
++        @Override
++        public boolean handleException(RuntimeException e) {
++            if (mDelegate != null) {
++                return mDelegate.handleException(e);
++            }
++            throw e;
++        }
++
++        private DefaultExceptionHandler() {}
++
++        /**
++         * Static class that implements the initialization-on-demand holder idiom.
++         */
++        private static class LazyHolder {
++            static final DefaultExceptionHandler INSTANCE = new DefaultExceptionHandler();
++        }
++
++        /**
++         * Gets the singleton instance for the DefaultExceptionHandler.
++         */
++        public static DefaultExceptionHandler getInstance() {
++            return LazyHolder.INSTANCE;
++        }
++
++        /**
++         * Sets a delegate ExceptionHandler, in case throwing an exception is not desirable.
++         */
++        public void setDelegate(ExceptionHandler exceptionHandler) {
++            mDelegate = exceptionHandler;
++        }
++    }
++}
+-- 
+2.18.0.203.gfac676dfb9-goog
+
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
index 0780641..850785a 100644
--- a/mojo/public/java/BUILD.gn
+++ b/mojo/public/java/BUILD.gn
@@ -37,6 +37,7 @@
     "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/ExceptionHandler.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",
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
index 2aa5ea6..d58602a 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -169,7 +169,9 @@
         ResultAnd<Boolean> result;
         do {
             try {
-                result = readAndDispatchMessage(mMessagePipeHandle, mIncomingMessageReceiver);
+                result = readAndDispatchMessage(
+                        mMessagePipeHandle, mIncomingMessageReceiver,
+                        ExceptionHandler.DefaultExceptionHandler.getInstance());
             } catch (MojoException e) {
                 onError(e);
                 return;
@@ -191,9 +193,11 @@
      *
      * @param receiver The {@link MessageReceiver} that will receive the read {@link Message}. Can
      *            be <code>null</code>, in which case the message is discarded.
+     * @param exceptionHandler The {@link ExceptionHandler} that can decide whether any uncaught
+     *            exception will close the connection or not.
      */
     static ResultAnd<Boolean> readAndDispatchMessage(
-            MessagePipeHandle handle, MessageReceiver receiver) {
+            MessagePipeHandle handle, MessageReceiver receiver, ExceptionHandler exceptionHandler) {
         // TODO(qsr) Allow usage of a pool of pre-allocated buffer for performance.
         ResultAnd<ReadMessageResult> result =
                 handle.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE);
@@ -206,7 +210,12 @@
         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()));
+            boolean accepted;
+            try {
+                accepted = receiver.accept(new Message(buffer, result.getValue().getHandles()));
+            } catch (RuntimeException e) {
+                accepted = exceptionHandler.handleException(e);
+            }
             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/ExceptionHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
new file mode 100644
index 0000000..8961d22
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
@@ -0,0 +1,59 @@
+// Copyright 2018 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;
+
+/**
+ * An {@link ExceptionHandler} is notified of any {@link RuntimeException} happening in the
+ * bindings or any of the callbacks.
+ */
+public interface ExceptionHandler {
+    /**
+     * Receives a notification that an unhandled {@link RuntimeException} has been thrown in an
+     * {@link Interface} implementation or one of the {@link Callbacks} internal classes.
+     *
+     * Normal implementations should either throw the exception or return whether the connection
+     * should be kept alive or terminated.
+     */
+    public boolean handleException(RuntimeException e);
+
+    /**
+     * The default ExceptionHandler, which simply throws the exception upon receiving it. It can
+     * also delegate the handling of the exceptions to another instance of ExceptionHandler.
+     */
+    public static class DefaultExceptionHandler implements ExceptionHandler {
+        private ExceptionHandler mDelegate;
+
+        @Override
+        public boolean handleException(RuntimeException e) {
+            if (mDelegate != null) {
+                return mDelegate.handleException(e);
+            }
+            throw e;
+        }
+
+        private DefaultExceptionHandler() {}
+
+        /**
+         * Static class that implements the initialization-on-demand holder idiom.
+         */
+        private static class LazyHolder {
+            static final DefaultExceptionHandler INSTANCE = new DefaultExceptionHandler();
+        }
+
+        /**
+         * Gets the singleton instance for the DefaultExceptionHandler.
+         */
+        public static DefaultExceptionHandler getInstance() {
+            return LazyHolder.INSTANCE;
+        }
+
+        /**
+         * Sets a delegate ExceptionHandler, in case throwing an exception is not desirable.
+         */
+        public void setDelegate(ExceptionHandler exceptionHandler) {
+            mDelegate = exceptionHandler;
+        }
+    }
+}