| OLD | NEW |
| (Empty) |
| 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.AsyncWaiter; | |
| 8 import org.chromium.mojo.system.Core; | |
| 9 import org.chromium.mojo.system.MessagePipeHandle; | |
| 10 | |
| 11 import java.util.HashMap; | |
| 12 import java.util.Map; | |
| 13 import java.util.concurrent.Executor; | |
| 14 | |
| 15 /** | |
| 16 * Implementation of {@link Router}. | |
| 17 */ | |
| 18 public class RouterImpl implements Router { | |
| 19 | |
| 20 /** | |
| 21 * {@link MessageReceiver} used as the {@link Connector} callback. | |
| 22 */ | |
| 23 private class HandleIncomingMessageThunk implements MessageReceiver { | |
| 24 | |
| 25 /** | |
| 26 * @see MessageReceiver#accept(Message) | |
| 27 */ | |
| 28 @Override | |
| 29 public boolean accept(Message message) { | |
| 30 return handleIncomingMessage(message); | |
| 31 } | |
| 32 | |
| 33 /** | |
| 34 * @see MessageReceiver#close() | |
| 35 */ | |
| 36 @Override | |
| 37 public void close() { | |
| 38 handleConnectorClose(); | |
| 39 } | |
| 40 | |
| 41 } | |
| 42 | |
| 43 /** | |
| 44 * | |
| 45 * {@link MessageReceiver} used to return responses to the caller. | |
| 46 */ | |
| 47 class ResponderThunk implements MessageReceiver { | |
| 48 private boolean mAcceptWasInvoked = false; | |
| 49 | |
| 50 /** | |
| 51 * @see | |
| 52 * MessageReceiver#accept(Message) | |
| 53 */ | |
| 54 @Override | |
| 55 public boolean accept(Message message) { | |
| 56 mAcceptWasInvoked = true; | |
| 57 return RouterImpl.this.accept(message); | |
| 58 } | |
| 59 | |
| 60 /** | |
| 61 * @see MessageReceiver#close() | |
| 62 */ | |
| 63 @Override | |
| 64 public void close() { | |
| 65 RouterImpl.this.close(); | |
| 66 } | |
| 67 | |
| 68 @Override | |
| 69 protected void finalize() throws Throwable { | |
| 70 if (!mAcceptWasInvoked) { | |
| 71 // We close the pipe here as a way of signaling to the calling a
pplication that an | |
| 72 // error condition occurred. Without this the calling applicatio
n would have no | |
| 73 // way of knowing it should stop waiting for a response. | |
| 74 RouterImpl.this.closeOnHandleThread(); | |
| 75 } | |
| 76 super.finalize(); | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 /** | |
| 81 * The {@link Connector} which is connected to the handle. | |
| 82 */ | |
| 83 private final Connector mConnector; | |
| 84 | |
| 85 /** | |
| 86 * The {@link MessageReceiverWithResponder} that will consume the messages r
eceived from the | |
| 87 * pipe. | |
| 88 */ | |
| 89 private MessageReceiverWithResponder mIncomingMessageReceiver; | |
| 90 | |
| 91 /** | |
| 92 * The next id to use for a request id which needs a response. It is auto-in
cremented. | |
| 93 */ | |
| 94 private long mNextRequestId = 1; | |
| 95 | |
| 96 /** | |
| 97 * The map from request ids to {@link MessageReceiver} of request currently
in flight. | |
| 98 */ | |
| 99 private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageRe
ceiver>(); | |
| 100 | |
| 101 /** | |
| 102 * An Executor that will run on the thread associated with the MessagePipe t
o which | |
| 103 * this Router is bound. This may be {@code Null} if the MessagePipeHandle p
assed | |
| 104 * in to the constructor is not valid. | |
| 105 */ | |
| 106 private final Executor mExecutor; | |
| 107 | |
| 108 /** | |
| 109 * Constructor that will use the default {@link AsyncWaiter}. | |
| 110 * | |
| 111 * @param messagePipeHandle The {@link MessagePipeHandle} to route message f
or. | |
| 112 */ | |
| 113 public RouterImpl(MessagePipeHandle messagePipeHandle) { | |
| 114 this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(me
ssagePipeHandle)); | |
| 115 } | |
| 116 | |
| 117 /** | |
| 118 * Constructor. | |
| 119 * | |
| 120 * @param messagePipeHandle The {@link MessagePipeHandle} to route message f
or. | |
| 121 * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of
new messages on the | |
| 122 * handle. | |
| 123 */ | |
| 124 public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWait
er) { | |
| 125 mConnector = new Connector(messagePipeHandle, asyncWaiter); | |
| 126 mConnector.setIncomingMessageReceiver(new HandleIncomingMessageThunk()); | |
| 127 Core core = messagePipeHandle.getCore(); | |
| 128 if (core != null) { | |
| 129 mExecutor = ExecutorFactory.getExecutorForCurrentThread(core); | |
| 130 } else { | |
| 131 mExecutor = null; | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 /** | |
| 136 * @see org.chromium.mojo.bindings.Router#start() | |
| 137 */ | |
| 138 @Override | |
| 139 public void start() { | |
| 140 mConnector.start(); | |
| 141 } | |
| 142 | |
| 143 /** | |
| 144 * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder) | |
| 145 */ | |
| 146 @Override | |
| 147 public void setIncomingMessageReceiver(MessageReceiverWithResponder incoming
MessageReceiver) { | |
| 148 this.mIncomingMessageReceiver = incomingMessageReceiver; | |
| 149 } | |
| 150 | |
| 151 /** | |
| 152 * @see MessageReceiver#accept(Message) | |
| 153 */ | |
| 154 @Override | |
| 155 public boolean accept(Message message) { | |
| 156 // A message without responder is directly forwarded to the connector. | |
| 157 return mConnector.accept(message); | |
| 158 } | |
| 159 | |
| 160 /** | |
| 161 * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageRec
eiver) | |
| 162 */ | |
| 163 @Override | |
| 164 public boolean acceptWithResponder(Message message, MessageReceiver responde
r) { | |
| 165 // The message must have a header. | |
| 166 ServiceMessage messageWithHeader = message.asServiceMessage(); | |
| 167 // Checking the message expects a response. | |
| 168 assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPEC
TS_RESPONSE_FLAG); | |
| 169 | |
| 170 // Compute a request id for being able to route the response. | |
| 171 long requestId = mNextRequestId++; | |
| 172 // Reserve 0 in case we want it to convey special meaning in the future. | |
| 173 if (requestId == 0) { | |
| 174 requestId = mNextRequestId++; | |
| 175 } | |
| 176 if (mResponders.containsKey(requestId)) { | |
| 177 throw new IllegalStateException("Unable to find a new request identi
fier."); | |
| 178 } | |
| 179 messageWithHeader.setRequestId(requestId); | |
| 180 mResponders.put(requestId, responder); | |
| 181 if (!mConnector.accept(messageWithHeader)) { | |
| 182 // Only keep the responder is the message has been accepted. | |
| 183 mResponders.remove(requestId); | |
| 184 return false; | |
| 185 } | |
| 186 return true; | |
| 187 } | |
| 188 | |
| 189 /** | |
| 190 * @see org.chromium.mojo.bindings.HandleOwner#passHandle() | |
| 191 */ | |
| 192 @Override | |
| 193 public MessagePipeHandle passHandle() { | |
| 194 return mConnector.passHandle(); | |
| 195 } | |
| 196 | |
| 197 /** | |
| 198 * @see java.io.Closeable#close() | |
| 199 */ | |
| 200 @Override | |
| 201 public void close() { | |
| 202 mConnector.close(); | |
| 203 } | |
| 204 | |
| 205 /** | |
| 206 * @see Router#setErrorHandler(ConnectionErrorHandler) | |
| 207 */ | |
| 208 @Override | |
| 209 public void setErrorHandler(ConnectionErrorHandler errorHandler) { | |
| 210 mConnector.setErrorHandler(errorHandler); | |
| 211 } | |
| 212 | |
| 213 /** | |
| 214 * Receive a message from the connector. Returns |true| if the message has b
een handled. | |
| 215 */ | |
| 216 private boolean handleIncomingMessage(Message message) { | |
| 217 MessageHeader header = message.asServiceMessage().getHeader(); | |
| 218 if (header.hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG)) { | |
| 219 if (mIncomingMessageReceiver != null) { | |
| 220 return mIncomingMessageReceiver.acceptWithResponder(message, new
ResponderThunk()); | |
| 221 } | |
| 222 // If we receive a request expecting a response when the client is n
ot | |
| 223 // listening, then we have no choice but to tear down the pipe. | |
| 224 close(); | |
| 225 return false; | |
| 226 } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { | |
| 227 long requestId = header.getRequestId(); | |
| 228 MessageReceiver responder = mResponders.get(requestId); | |
| 229 if (responder == null) { | |
| 230 return false; | |
| 231 } | |
| 232 mResponders.remove(requestId); | |
| 233 return responder.accept(message); | |
| 234 } else { | |
| 235 if (mIncomingMessageReceiver != null) { | |
| 236 return mIncomingMessageReceiver.accept(message); | |
| 237 } | |
| 238 // OK to drop the message. | |
| 239 } | |
| 240 return false; | |
| 241 } | |
| 242 | |
| 243 private void handleConnectorClose() { | |
| 244 if (mIncomingMessageReceiver != null) { | |
| 245 mIncomingMessageReceiver.close(); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * Invokes {@link #close()} asynchronously on the thread associated with | |
| 251 * this Router's Handle. If this Router was constructed with an invalid | |
| 252 * handle then this method does nothing. | |
| 253 */ | |
| 254 private void closeOnHandleThread() { | |
| 255 if (mExecutor != null) { | |
| 256 mExecutor.execute(new Runnable() { | |
| 257 | |
| 258 @Override | |
| 259 public void run() { | |
| 260 close(); | |
| 261 } | |
| 262 }); | |
| 263 } | |
| 264 } | |
| 265 } | |
| OLD | NEW |