1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.mojo.bindings; 6 7 import org.chromium.mojo.system.Core; 8 import org.chromium.mojo.system.MessagePipeHandle; 9 import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult; 10 import org.chromium.mojo.system.MojoException; 11 import org.chromium.mojo.system.MojoResult; 12 import org.chromium.mojo.system.ResultAnd; 13 import org.chromium.mojo.system.Watcher; 14 15 import java.nio.ByteBuffer; 16 17 /** 18 * A {@link Connector} owns a {@link MessagePipeHandle} and will send any received messages to the 19 * registered {@link MessageReceiver}. It also acts as a {@link MessageReceiver} and will send any 20 * message through the handle. 21 * <p> 22 * The method |start| must be called before the {@link Connector} will start listening to incoming 23 * messages. 24 */ 25 public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle> { 26 27 /** 28 * The callback that is notified when the state of the owned handle changes. 29 */ 30 private final WatcherCallback mWatcherCallback = new WatcherCallback(); 31 32 /** 33 * The owned message pipe. 34 */ 35 private final MessagePipeHandle mMessagePipeHandle; 36 37 /** 38 * A watcher which is notified when a new message is available on the owned message pipe. 39 */ 40 private final Watcher mWatcher; 41 42 /** 43 * The {@link MessageReceiver} to which received messages are sent. 44 */ 45 private MessageReceiver mIncomingMessageReceiver; 46 47 /** 48 * The error handler to notify of errors. 49 */ 50 private ConnectionErrorHandler mErrorHandler; 51 52 /** 53 * Create a new connector over a |messagePipeHandle|. The created connector will use the default 54 * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|. 55 */ Connector(MessagePipeHandle messagePipeHandle)56 public Connector(MessagePipeHandle messagePipeHandle) { 57 this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle)); 58 } 59 60 /** 61 * Create a new connector over a |messagePipeHandle| using the given {@link AsyncWaiter} to get 62 * notified of changes on the handle. 63 */ Connector(MessagePipeHandle messagePipeHandle, Watcher watcher)64 public Connector(MessagePipeHandle messagePipeHandle, Watcher watcher) { 65 mMessagePipeHandle = messagePipeHandle; 66 mWatcher = watcher; 67 } 68 69 /** 70 * Set the {@link MessageReceiver} that will receive message from the owned message pipe. 71 */ setIncomingMessageReceiver(MessageReceiver incomingMessageReceiver)72 public void setIncomingMessageReceiver(MessageReceiver incomingMessageReceiver) { 73 mIncomingMessageReceiver = incomingMessageReceiver; 74 } 75 76 /** 77 * Set the {@link ConnectionErrorHandler} that will be notified of errors on the owned message 78 * pipe. 79 */ setErrorHandler(ConnectionErrorHandler errorHandler)80 public void setErrorHandler(ConnectionErrorHandler errorHandler) { 81 mErrorHandler = errorHandler; 82 } 83 84 /** 85 * Start listening for incoming messages. 86 */ start()87 public void start() { 88 mWatcher.start(mMessagePipeHandle, Core.HandleSignals.READABLE, mWatcherCallback); 89 } 90 91 /** 92 * @see MessageReceiver#accept(Message) 93 */ 94 @Override accept(Message message)95 public boolean accept(Message message) { 96 try { 97 mMessagePipeHandle.writeMessage(message.getData(), 98 message.getHandles(), MessagePipeHandle.WriteFlags.NONE); 99 return true; 100 } catch (MojoException e) { 101 onError(e); 102 return false; 103 } 104 } 105 106 /** 107 * Pass the owned handle of the connector. After this, the connector is disconnected. It cannot 108 * accept new message and it isn't listening to the handle anymore. 109 * 110 * @see org.chromium.mojo.bindings.HandleOwner#passHandle() 111 */ 112 @Override passHandle()113 public MessagePipeHandle passHandle() { 114 cancelIfActive(); 115 MessagePipeHandle handle = mMessagePipeHandle.pass(); 116 if (mIncomingMessageReceiver != null) { 117 mIncomingMessageReceiver.close(); 118 } 119 return handle; 120 } 121 122 /** 123 * @see java.io.Closeable#close() 124 */ 125 @Override close()126 public void close() { 127 cancelIfActive(); 128 mMessagePipeHandle.close(); 129 if (mIncomingMessageReceiver != null) { 130 MessageReceiver incomingMessageReceiver = mIncomingMessageReceiver; 131 mIncomingMessageReceiver = null; 132 incomingMessageReceiver.close(); 133 } 134 } 135 136 private class WatcherCallback implements Watcher.Callback { 137 /** 138 * @see org.chromium.mojo.system.Watcher.Callback#onResult(int) 139 */ 140 @Override onResult(int result)141 public void onResult(int result) { 142 Connector.this.onWatcherResult(result); 143 } 144 145 } 146 147 /** 148 * @see org.chromium.mojo.system.Watcher.Callback#onResult(int) 149 */ onWatcherResult(int result)150 private void onWatcherResult(int result) { 151 if (result == MojoResult.OK) { 152 readOutstandingMessages(); 153 } else { 154 onError(new MojoException(result)); 155 } 156 } 157 onError(MojoException exception)158 private void onError(MojoException exception) { 159 close(); 160 if (mErrorHandler != null) { 161 mErrorHandler.onConnectionError(exception); 162 } 163 } 164 165 /** 166 * Read all available messages on the owned message pipe. 167 */ readOutstandingMessages()168 private void readOutstandingMessages() { 169 ResultAnd<Boolean> result; 170 do { 171 try { 172 result = readAndDispatchMessage(mMessagePipeHandle, mIncomingMessageReceiver); 173 } catch (MojoException e) { 174 onError(e); 175 return; 176 } 177 } while (result.getValue()); 178 if (result.getMojoResult() != MojoResult.SHOULD_WAIT) { 179 onError(new MojoException(result.getMojoResult())); 180 } 181 } 182 cancelIfActive()183 private void cancelIfActive() { 184 mWatcher.cancel(); 185 mWatcher.destroy(); 186 } 187 188 /** 189 * Read a message, and pass it to the given |MessageReceiver| if not null. If the 190 * |MessageReceiver| is null, the message is lost. 191 * 192 * @param receiver The {@link MessageReceiver} that will receive the read {@link Message}. Can 193 * be <code>null</code>, in which case the message is discarded. 194 */ readAndDispatchMessage( MessagePipeHandle handle, MessageReceiver receiver)195 static ResultAnd<Boolean> readAndDispatchMessage( 196 MessagePipeHandle handle, MessageReceiver receiver) { 197 ResultAnd<ReadMessageResult> result = handle.readMessage(MessagePipeHandle.ReadFlags.NONE); 198 if (result.getMojoResult() != MojoResult.OK) { 199 return new ResultAnd<Boolean>(result.getMojoResult(), false); 200 } 201 ReadMessageResult readResult = result.getValue(); 202 assert readResult != null; 203 if (receiver != null) { 204 boolean accepted; 205 try { 206 accepted = receiver.accept( 207 new Message(ByteBuffer.wrap(readResult.mData), readResult.mHandles)); 208 } catch (RuntimeException e) { 209 // The DefaultExceptionHandler will decide whether any uncaught exception will 210 // close the connection or not. 211 accepted = 212 ExceptionHandler.DefaultExceptionHandler.getInstance().handleException(e); 213 } 214 return new ResultAnd<Boolean>(result.getMojoResult(), accepted); 215 } 216 return new ResultAnd<Boolean>(result.getMojoResult(), false); 217 } 218 } 219