| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.polo.wire.protobuf; |
| |
| import com.google.polo.exception.BadSecretException; |
| import com.google.polo.exception.NoConfigurationException; |
| import com.google.polo.exception.PoloException; |
| import com.google.polo.exception.ProtocolErrorException; |
| import com.google.polo.pairing.PairingContext; |
| import com.google.polo.pairing.PoloUtil; |
| import com.google.polo.pairing.message.ConfigurationAckMessage; |
| import com.google.polo.pairing.message.ConfigurationMessage; |
| import com.google.polo.pairing.message.EncodingOption; |
| import com.google.polo.pairing.message.OptionsMessage; |
| import com.google.polo.pairing.message.PairingRequestAckMessage; |
| import com.google.polo.pairing.message.PairingRequestMessage; |
| import com.google.polo.pairing.message.PoloMessage; |
| import com.google.polo.pairing.message.SecretAckMessage; |
| import com.google.polo.pairing.message.SecretMessage; |
| import com.google.polo.wire.PoloWireInterface; |
| import com.google.polo.wire.protobuf.nano.PoloProto; |
| import com.google.polo.wire.protobuf.nano.PoloProto.OuterMessage; |
| import com.google.protobuf.nano.MessageNano; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| |
| /** |
| * Implementation of {@link PoloWireInterface} that uses Protocol Buffers for |
| * the data representation. |
| * <p/> |
| * The primary work of this class is to translate Protocol Buffer messages |
| * instances (derived from {@link MessageNano} to an internal message |
| * instance (derived from {@link PoloMessage}, and vice versa. |
| * <p/> |
| * The reason we are going through all this trouble, and not using protocol |
| * buffer objects directly, is that we'd like to limit the scope of protocol |
| * buffers to the wire protocol only. Some applications may prefer to use |
| * a different wire format, where the requirement of adding the protobuf library |
| * could be an impediment. |
| */ |
| public class ProtobufWireAdapter implements PoloWireInterface { |
| |
| /** |
| * The output coming from the peer. |
| */ |
| private final InputStream mInputStream; |
| /** |
| * The input going to the peer. |
| */ |
| private final OutputStream mOutputStream; |
| |
| /** |
| * Constructor. |
| * |
| * @param input the {@link InputStream} from the peer |
| * @param output the {@link OutputStream} to the peer |
| */ |
| public ProtobufWireAdapter(InputStream input, OutputStream output) { |
| mInputStream = input; |
| mOutputStream = output; |
| } |
| |
| /** |
| * Generates a new instance from a {@link PairingContext}. |
| * |
| * @param context the {@link PairingContext} |
| * @return the new instance |
| */ |
| public static ProtobufWireAdapter fromContext(PairingContext context) { |
| return new ProtobufWireAdapter(context.getPeerInputStream(), |
| context.getPeerOutputStream()); |
| } |
| |
| /** |
| * Returns the next message sent over the wire, blocking as necessary. |
| */ |
| public PoloMessage getNextMessage() throws IOException, PoloException { |
| return protoToPoloMessage(readNextInnerMessage()); |
| } |
| |
| /** |
| * Returns the next message read over the wire, requiring it to be a certain |
| * type. |
| * |
| * @param type the required message type |
| * @throws IOException on error during read |
| * @throws PoloException if the wrong message type was read, or on protocol |
| * error |
| */ |
| public PoloMessage getNextMessage(PoloMessage.PoloMessageType type) |
| throws IOException, PoloException { |
| PoloMessage message = getNextMessage(); |
| if (message.getType() != type) { |
| throw new PoloException("Wrong message type (wanted " + type + |
| ", got " + message.getType() + ")"); |
| } |
| return message; |
| } |
| |
| /** |
| * Returns the next message seen on the input stream. |
| * |
| * @return the next OuterMessage read from the wire |
| * @throws IOException on error during read |
| */ |
| private OuterMessage readNextOuterMessage() throws IOException, PoloException { |
| // Read the preamble (length of payload) |
| byte[] preambleBuffer = readBytesBlocking(4); |
| int messageLen = (int) PoloUtil.intBigEndianBytesToLong(preambleBuffer); |
| |
| // Read the payload (serialized PoloMessage) |
| byte[] messageBuffer = readBytesBlocking(messageLen); |
| |
| // Decode and return the payload |
| OuterMessage message = OuterMessage.parseFrom(messageBuffer); |
| |
| if (message.status != OuterMessage.STATUS_OK) { |
| throw new ProtocolErrorException(); |
| } |
| |
| return message; |
| } |
| |
| /** |
| * Reads the next inner message from the wire, decoding and handling the outer |
| * message in the process. |
| * |
| * @return a protocol buffer message |
| * @throws IOException on error during read |
| * @throws PoloException on protocol error |
| */ |
| private MessageNano readNextInnerMessage() |
| throws IOException, PoloException { |
| OuterMessage message = readNextOuterMessage(); |
| |
| byte[] payload = message.payload; |
| |
| if (message.type == OuterMessage.MESSAGE_TYPE_OPTIONS) { |
| return PoloProto.Options.parseFrom(payload); |
| } else if (message.type == OuterMessage.MESSAGE_TYPE_PAIRING_REQUEST) { |
| return PoloProto.PairingRequest.parseFrom(payload); |
| } else if (message.type == OuterMessage.MESSAGE_TYPE_PAIRING_REQUEST_ACK) { |
| return PoloProto.PairingRequestAck.parseFrom(payload); |
| } else if (message.type == OuterMessage.MESSAGE_TYPE_CONFIGURATION) { |
| return PoloProto.Configuration.parseFrom(payload); |
| } else if (message.type == OuterMessage.MESSAGE_TYPE_CONFIGURATION_ACK) { |
| return PoloProto.ConfigurationAck.parseFrom(payload); |
| } else if (message.type == OuterMessage.MESSAGE_TYPE_SECRET) { |
| return PoloProto.Secret.parseFrom(payload); |
| } else if (message.type == OuterMessage.MESSAGE_TYPE_SECRET_ACK) { |
| return PoloProto.SecretAck.parseFrom(payload); |
| } |
| |
| throw new IOException("Could not unparse message"); |
| } |
| |
| /** |
| * Convenience method to read a fixed number of bytes from the client |
| * InputStream, blocking if necessary. |
| * |
| * @param numBytes the number of bytes to read |
| * @return the bytes read |
| * @throws IOException on error during read |
| */ |
| private byte[] readBytesBlocking(int numBytes) throws IOException { |
| byte[] buf = new byte[numBytes]; |
| int bytesRead = 0; |
| |
| // For an SSLSocket, read() can frequently return zero bytes, |
| // or fewer bytes than desired, due to SSL unwrapping and other |
| // non-application-data events. |
| while (bytesRead < numBytes) { |
| int inc = mInputStream.read(buf, bytesRead, numBytes - bytesRead); |
| if (inc < 0) { |
| throw new IOException("Stream closed while reading."); |
| } |
| bytesRead += inc; |
| } |
| return buf; |
| } |
| |
| /** |
| * Wraps an outer message in an inner message. |
| * |
| * @param message the {@link MessageNano} to wrap |
| * @throws PoloException if the message was not well formed |
| */ |
| private OuterMessage wrapInnerMessage(MessageNano message) |
| throws PoloException { |
| int type; |
| if (message instanceof PoloProto.Options) { |
| type = OuterMessage.MESSAGE_TYPE_OPTIONS; |
| } else if (message instanceof PoloProto.PairingRequest) { |
| type = OuterMessage.MESSAGE_TYPE_PAIRING_REQUEST; |
| } else if (message instanceof PoloProto.PairingRequestAck) { |
| type = OuterMessage.MESSAGE_TYPE_PAIRING_REQUEST_ACK; |
| } else if (message instanceof PoloProto.Configuration) { |
| type = OuterMessage.MESSAGE_TYPE_CONFIGURATION; |
| } else if (message instanceof PoloProto.ConfigurationAck) { |
| type = OuterMessage.MESSAGE_TYPE_CONFIGURATION_ACK; |
| } else if (message instanceof PoloProto.Secret) { |
| type = OuterMessage.MESSAGE_TYPE_SECRET; |
| } else if (message instanceof PoloProto.SecretAck) { |
| type = OuterMessage.MESSAGE_TYPE_SECRET_ACK; |
| } else { |
| throw new PoloException("Bad inner message type."); |
| } |
| |
| // compose outer message |
| OuterMessage outerMessage = new OuterMessage(); |
| outerMessage.status = OuterMessage.STATUS_OK; |
| outerMessage.protocolVersion = 1; |
| outerMessage.type = type; |
| outerMessage.payload = MessageNano.toByteArray(message); |
| return outerMessage; |
| } |
| |
| /** |
| * Writes an {@link OuterMessage} to the wire. |
| * |
| * @param message the message |
| * @throws IOException on error during write |
| */ |
| private void writeMessage(OuterMessage message) throws IOException { |
| byte[] messageBytes = message.payload; |
| int messageLength = messageBytes.length; |
| |
| mOutputStream.write(PoloUtil.intToBigEndianIntBytes(messageLength)); |
| mOutputStream.write(messageBytes); |
| } |
| |
| /** |
| * Writes a new message to the wire. |
| */ |
| public void sendMessage(PoloMessage message) |
| throws IOException, PoloException { |
| MessageNano pb = poloMessageToProto(message); |
| OuterMessage outerMessage = wrapInnerMessage(pb); |
| writeMessage(outerMessage); |
| } |
| |
| /** |
| * Sends a new error message to the wire. |
| */ |
| public void sendErrorMessage(Exception e) throws IOException { |
| OuterMessage outerMessage = new OuterMessage(); |
| outerMessage.protocolVersion = 1; |
| |
| if (e instanceof NoConfigurationException) { |
| outerMessage.status = OuterMessage.STATUS_BAD_CONFIGURATION; |
| } else if (e instanceof BadSecretException) { |
| outerMessage.status = OuterMessage.STATUS_BAD_SECRET; |
| } else { |
| outerMessage.status = OuterMessage.STATUS_ERROR; |
| } |
| |
| writeMessage(outerMessage); |
| } |
| |
| /** |
| * Converts an internal message to the corresponding protocol buffer message. |
| * |
| * @param poloMessage the internal message |
| * @return a new {@link MessageNano} instance |
| */ |
| private MessageNano poloMessageToProto(PoloMessage poloMessage) { |
| if (poloMessage instanceof PairingRequestMessage) { |
| return toProto((PairingRequestMessage) poloMessage); |
| } else if (poloMessage instanceof PairingRequestAckMessage) { |
| return toProto((PairingRequestAckMessage) poloMessage); |
| } else if (poloMessage instanceof OptionsMessage) { |
| return toProto((OptionsMessage) poloMessage); |
| } else if (poloMessage instanceof ConfigurationMessage) { |
| return toProto((ConfigurationMessage) poloMessage); |
| } else if (poloMessage instanceof ConfigurationAckMessage) { |
| return toProto((ConfigurationAckMessage) poloMessage); |
| } else if (poloMessage instanceof SecretMessage) { |
| return toProto((SecretMessage) poloMessage); |
| } else if (poloMessage instanceof SecretAckMessage) { |
| return toProto((SecretAckMessage) poloMessage); |
| } |
| return null; |
| } |
| |
| /** |
| * Converts a {@link PairingRequestMessage} to a |
| * {@link PoloProto.PairingRequest}. |
| */ |
| private PoloProto.PairingRequest toProto(PairingRequestMessage poloMessage) { |
| PoloProto.PairingRequest pairingRequest = new PoloProto.PairingRequest(); |
| pairingRequest.serviceName = poloMessage.getServiceName(); |
| |
| if (poloMessage.hasClientName()) { |
| pairingRequest.clientName = poloMessage.getClientName(); |
| } |
| return pairingRequest; |
| } |
| |
| /** |
| * Converts a {@link PairingRequestAckMessage} to a |
| * {@link PoloProto.PairingRequestAck}. |
| */ |
| private PoloProto.PairingRequestAck toProto(PairingRequestAckMessage poloMessage) { |
| PoloProto.PairingRequestAck pairingRequestAck = new PoloProto.PairingRequestAck(); |
| if (poloMessage.hasServerName()) { |
| pairingRequestAck.serverName = poloMessage.getServerName(); |
| } |
| return pairingRequestAck; |
| } |
| |
| /** |
| * Converts a {@link OptionsMessage} to a {@link PoloProto.Options}. |
| */ |
| private PoloProto.Options toProto(OptionsMessage poloMessage) { |
| PoloProto.Options options = new PoloProto.Options(); |
| |
| switch (poloMessage.getProtocolRolePreference()) { |
| case DISPLAY_DEVICE: |
| options.preferredRole = PoloProto.Options.ROLE_TYPE_INPUT; |
| break; |
| case INPUT_DEVICE: |
| options.preferredRole = PoloProto.Options.ROLE_TYPE_OUTPUT; |
| break; |
| } |
| |
| int i = 0, n = poloMessage.getOutputEncodingSet().size(); |
| options.outputEncodings = new PoloProto.Options.Encoding[n]; |
| for (EncodingOption enc : poloMessage.getOutputEncodingSet()) { |
| options.outputEncodings[i++] = toProto(enc); |
| } |
| |
| i = 0; |
| n = poloMessage.getInputEncodingSet().size(); |
| options.inputEncodings = new PoloProto.Options.Encoding[n]; |
| for (EncodingOption enc : poloMessage.getInputEncodingSet()) { |
| options.inputEncodings[i++] = toProto(enc); |
| } |
| |
| return options; |
| } |
| |
| /** |
| * Converts a {@link ConfigurationMessage} to a |
| * {@link PoloProto.Configuration}. |
| */ |
| private PoloProto.Configuration toProto(ConfigurationMessage poloMessage) { |
| PoloProto.Configuration configuration = new PoloProto.Configuration(); |
| configuration.encoding = toProto(poloMessage.getEncoding()); |
| configuration.clientRole = toProto(poloMessage.getClientRole()); |
| return configuration; |
| } |
| |
| /** |
| * Converts a {@link EncodingOption} to a {@link PoloProto.Options.Encoding}. |
| */ |
| private PoloProto.Options.Encoding toProto(EncodingOption enc) { |
| PoloProto.Options.Encoding encoding = new PoloProto.Options.Encoding(); |
| |
| switch (enc.getType()) { |
| case ENCODING_ALPHANUMERIC: |
| encoding.type = PoloProto.Options.Encoding.ENCODING_TYPE_ALPHANUMERIC; |
| break; |
| case ENCODING_HEXADECIMAL: |
| encoding.type = PoloProto.Options.Encoding.ENCODING_TYPE_HEXADECIMAL; |
| break; |
| case ENCODING_NUMERIC: |
| encoding.type = PoloProto.Options.Encoding.ENCODING_TYPE_NUMERIC; |
| break; |
| case ENCODING_QRCODE: |
| encoding.type = PoloProto.Options.Encoding.ENCODING_TYPE_QRCODE; |
| break; |
| default: |
| encoding.type = PoloProto.Options.Encoding.ENCODING_TYPE_UNKNOWN; |
| break; |
| } |
| |
| encoding.symbolLength = enc.getSymbolLength(); |
| return encoding; |
| } |
| |
| /** |
| * Converts a {@link OptionsMessage.ProtocolRole} to a |
| * {@link PoloProto.Options}. |
| */ |
| private int toProto(OptionsMessage.ProtocolRole role) { |
| switch (role) { |
| case DISPLAY_DEVICE: |
| return PoloProto.Options.ROLE_TYPE_OUTPUT; |
| case INPUT_DEVICE: |
| return PoloProto.Options.ROLE_TYPE_INPUT; |
| default: |
| return PoloProto.Options.ROLE_TYPE_UNKNOWN; |
| } |
| } |
| |
| /** |
| * Converts a {@link ConfigurationAckMessage} to a |
| * {@link PoloProto.ConfigurationAck}. |
| */ |
| private PoloProto.ConfigurationAck toProto(ConfigurationAckMessage poloMessage) { |
| PoloProto.ConfigurationAck configurationAck = new PoloProto.ConfigurationAck(); |
| return configurationAck; |
| } |
| |
| /** |
| * Converts a {@link SecretMessage} to a {@link PoloProto.Secret}. |
| */ |
| private PoloProto.Secret toProto(SecretMessage poloMessage) { |
| PoloProto.Secret secret = new PoloProto.Secret(); |
| secret.secret = poloMessage.getSecret(); |
| return secret; |
| } |
| |
| /** |
| * Converts a {@link SecretAckMessage} to a {@link PoloProto.SecretAck}. |
| */ |
| private PoloProto.SecretAck toProto(SecretAckMessage poloMessage) { |
| PoloProto.SecretAck secretAck = new PoloProto.SecretAck(); |
| secretAck.secret = poloMessage.getSecret(); |
| return secretAck; |
| } |
| |
| // |
| // polo -> protocol buffer routines |
| // |
| |
| /** |
| * Converts a protocol buffer message to the corresponding internal |
| * message. |
| * |
| * @param protoMessage the protobuf message to convert |
| * @return the new {@link PoloMessage} |
| */ |
| private PoloMessage protoToPoloMessage(MessageNano protoMessage) { |
| if (protoMessage instanceof PoloProto.PairingRequest) { |
| return fromProto((PoloProto.PairingRequest) protoMessage); |
| } else if (protoMessage instanceof PoloProto.PairingRequestAck) { |
| return fromProto((PoloProto.PairingRequestAck) protoMessage); |
| } else if (protoMessage instanceof PoloProto.Options) { |
| return fromProto((PoloProto.Options) protoMessage); |
| } else if (protoMessage instanceof PoloProto.Configuration) { |
| return fromProto((PoloProto.Configuration) protoMessage); |
| } else if (protoMessage instanceof PoloProto.ConfigurationAck) { |
| return fromProto((PoloProto.ConfigurationAck) protoMessage); |
| } else if (protoMessage instanceof PoloProto.Secret) { |
| return fromProto((PoloProto.Secret) protoMessage); |
| } else if (protoMessage instanceof PoloProto.SecretAck) { |
| return fromProto((PoloProto.SecretAck) protoMessage); |
| } |
| return null; |
| } |
| |
| /** |
| * Converts a {@link PoloProto.PairingRequest} to a |
| * {@link PairingRequestMessage}. |
| */ |
| private PairingRequestMessage fromProto(PoloProto.PairingRequest protoMessage) { |
| return new PairingRequestMessage(protoMessage.serviceName, protoMessage.clientName); |
| } |
| |
| /** |
| * Converts a {@link PoloProto.PairingRequestAck} to a |
| * {@link PairingRequestAckMessage}. |
| */ |
| private PairingRequestAckMessage fromProto(PoloProto.PairingRequestAck protoMessage) { |
| return new PairingRequestAckMessage(protoMessage.serverName); |
| } |
| |
| /** |
| * Converts a {@link PoloProto.Options} to a {@link OptionsMessage}. |
| */ |
| private OptionsMessage fromProto(PoloProto.Options protoMessage) { |
| OptionsMessage optionsMessage = new OptionsMessage(); |
| |
| switch (protoMessage.preferredRole) { |
| case PoloProto.Options.ROLE_TYPE_INPUT: |
| optionsMessage.setProtocolRolePreference(OptionsMessage.ProtocolRole.INPUT_DEVICE); |
| break; |
| case PoloProto.Options.ROLE_TYPE_OUTPUT: |
| optionsMessage.setProtocolRolePreference(OptionsMessage.ProtocolRole.DISPLAY_DEVICE); |
| break; |
| } |
| |
| for (PoloProto.Options.Encoding e : protoMessage.inputEncodings) { |
| optionsMessage.addInputEncoding(fromProto(e)); |
| } |
| |
| for (PoloProto.Options.Encoding e : protoMessage.outputEncodings) { |
| optionsMessage.addOutputEncoding(fromProto(e)); |
| } |
| |
| return optionsMessage; |
| } |
| |
| /** |
| * Converts a {@link PoloProto.Configuration} to a |
| * {@link ConfigurationMessage}. |
| */ |
| private ConfigurationMessage fromProto(PoloProto.Configuration protoMessage) { |
| EncodingOption enc = fromProto(protoMessage.encoding); |
| OptionsMessage.ProtocolRole role = OptionsMessage.ProtocolRole.UNKNOWN; |
| |
| switch (protoMessage.clientRole) { |
| case PoloProto.Options.ROLE_TYPE_INPUT: |
| role = OptionsMessage.ProtocolRole.INPUT_DEVICE; |
| break; |
| case PoloProto.Options.ROLE_TYPE_OUTPUT: |
| role = OptionsMessage.ProtocolRole.DISPLAY_DEVICE; |
| break; |
| } |
| |
| return new ConfigurationMessage(enc, role); |
| } |
| |
| /** |
| * Converts a {@link PoloProto.ConfigurationAck} to a |
| * {@link ConfigurationAckMessage}. |
| */ |
| private ConfigurationAckMessage fromProto(PoloProto.ConfigurationAck protoMessage) { |
| return new ConfigurationAckMessage(); |
| } |
| |
| /** |
| * Converts a {@link PoloProto.Secret} to a {@link SecretMessage}. |
| */ |
| private SecretMessage fromProto(PoloProto.Secret protoMessage) { |
| return new SecretMessage(protoMessage.secret); |
| } |
| |
| /** |
| * Converts a {@link PoloProto.SecretAck} to a {@link SecretAckMessage}. |
| */ |
| private SecretAckMessage fromProto(PoloProto.SecretAck protoMessage) { |
| return new SecretAckMessage(protoMessage.secret); |
| } |
| |
| /** |
| * Converts a {@link PoloProto.Options.Encoding} to a {@link EncodingOption}. |
| */ |
| private EncodingOption fromProto(PoloProto.Options.Encoding enc) { |
| EncodingOption.EncodingType type; |
| |
| switch (enc.type) { |
| case PoloProto.Options.Encoding.ENCODING_TYPE_ALPHANUMERIC: |
| type = EncodingOption.EncodingType.ENCODING_ALPHANUMERIC; |
| break; |
| case PoloProto.Options.Encoding.ENCODING_TYPE_HEXADECIMAL: |
| type = EncodingOption.EncodingType.ENCODING_HEXADECIMAL; |
| break; |
| case PoloProto.Options.Encoding.ENCODING_TYPE_NUMERIC: |
| type = EncodingOption.EncodingType.ENCODING_NUMERIC; |
| break; |
| case PoloProto.Options.Encoding.ENCODING_TYPE_QRCODE: |
| type = EncodingOption.EncodingType.ENCODING_QRCODE; |
| break; |
| default: |
| type = EncodingOption.EncodingType.ENCODING_UNKNOWN; |
| } |
| |
| return new EncodingOption(type, enc.symbolLength); |
| |
| } |
| |
| } |