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 java.nio.ByteBuffer; 8 9 /** 10 * Header information for a message. 11 */ 12 public class MessageHeader { 13 14 private static final int SIMPLE_MESSAGE_SIZE = 24; 15 private static final int SIMPLE_MESSAGE_VERSION = 0; 16 private static final DataHeader SIMPLE_MESSAGE_STRUCT_INFO = 17 new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_VERSION); 18 19 private static final int MESSAGE_WITH_REQUEST_ID_SIZE = 32; 20 private static final int MESSAGE_WITH_REQUEST_ID_VERSION = 1; 21 private static final DataHeader MESSAGE_WITH_REQUEST_ID_STRUCT_INFO = 22 new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_VERSION); 23 24 private static final int INTERFACE_ID_OFFSET = 8; 25 private static final int TYPE_OFFSET = 12; 26 private static final int FLAGS_OFFSET = 16; 27 private static final int REQUEST_ID_OFFSET = 24; 28 29 /** 30 * Flag for a header of a simple message. 31 */ 32 public static final int NO_FLAG = 0; 33 34 /** 35 * Flag for a header of a message that expected a response. 36 */ 37 public static final int MESSAGE_EXPECTS_RESPONSE_FLAG = 1 << 0; 38 39 /** 40 * Flag for a header of a message that is a response. 41 */ 42 public static final int MESSAGE_IS_RESPONSE_FLAG = 1 << 1; 43 44 private final DataHeader mDataHeader; 45 private final int mType; 46 private final int mFlags; 47 private long mRequestId; 48 49 /** 50 * Constructor for the header of a message which does not have a response. 51 */ MessageHeader(int type)52 public MessageHeader(int type) { 53 mDataHeader = SIMPLE_MESSAGE_STRUCT_INFO; 54 mType = type; 55 mFlags = 0; 56 mRequestId = 0; 57 } 58 59 /** 60 * Constructor for the header of a message which have a response or being itself a response. 61 */ MessageHeader(int type, int flags, long requestId)62 public MessageHeader(int type, int flags, long requestId) { 63 assert mustHaveRequestId(flags); 64 mDataHeader = MESSAGE_WITH_REQUEST_ID_STRUCT_INFO; 65 mType = type; 66 mFlags = flags; 67 mRequestId = requestId; 68 } 69 70 /** 71 * Constructor, parsing the header from a message. Should only be used by {@link Message} 72 * itself. 73 */ MessageHeader(Message message)74 MessageHeader(Message message) { 75 Decoder decoder = new Decoder(message); 76 mDataHeader = decoder.readDataHeader(); 77 validateDataHeader(mDataHeader); 78 79 // Currently associated interfaces are not supported. 80 int interfaceId = decoder.readInt(INTERFACE_ID_OFFSET); 81 if (interfaceId != 0) { 82 throw new DeserializationException("Non-zero interface ID, expecting zero since " 83 + "associated interfaces are not yet supported."); 84 } 85 86 mType = decoder.readInt(TYPE_OFFSET); 87 mFlags = decoder.readInt(FLAGS_OFFSET); 88 if (mustHaveRequestId(mFlags)) { 89 if (mDataHeader.size < MESSAGE_WITH_REQUEST_ID_SIZE) { 90 throw new DeserializationException("Incorrect message size, expecting at least " 91 + MESSAGE_WITH_REQUEST_ID_SIZE 92 + " for a message with a request identifier, but got: " + mDataHeader.size); 93 94 } 95 mRequestId = decoder.readLong(REQUEST_ID_OFFSET); 96 } else { 97 mRequestId = 0; 98 } 99 } 100 101 /** 102 * Returns the size in bytes of the serialization of the header. 103 */ getSize()104 public int getSize() { 105 return mDataHeader.size; 106 } 107 108 /** 109 * Returns the type of the message. 110 */ getType()111 public int getType() { 112 return mType; 113 } 114 115 /** 116 * Returns the flags associated to the message. 117 */ getFlags()118 public int getFlags() { 119 return mFlags; 120 } 121 122 /** 123 * Returns if the message has the given flag. 124 */ hasFlag(int flag)125 public boolean hasFlag(int flag) { 126 return (mFlags & flag) == flag; 127 } 128 129 /** 130 * Returns if the message has a request id. 131 */ hasRequestId()132 public boolean hasRequestId() { 133 return mustHaveRequestId(mFlags); 134 } 135 136 /** 137 * Return the request id for the message. Must only be called if the message has a request id. 138 */ getRequestId()139 public long getRequestId() { 140 assert hasRequestId(); 141 return mRequestId; 142 } 143 144 /** 145 * Encode the header. 146 */ encode(Encoder encoder)147 public void encode(Encoder encoder) { 148 encoder.encode(mDataHeader); 149 // Set the interface ID field to 0. 150 encoder.encode(0, INTERFACE_ID_OFFSET); 151 encoder.encode(getType(), TYPE_OFFSET); 152 encoder.encode(getFlags(), FLAGS_OFFSET); 153 if (hasRequestId()) { 154 encoder.encode(getRequestId(), REQUEST_ID_OFFSET); 155 } 156 } 157 158 /** 159 * Returns true if the header has the expected flags. Only considers flags this class knows 160 * about in order to allow this class to work with future version of the header format. 161 */ validateHeader(int expectedFlags)162 public boolean validateHeader(int expectedFlags) { 163 int knownFlags = getFlags() & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG); 164 return knownFlags == expectedFlags; 165 } 166 167 /** 168 * Returns true if the header has the expected type and flags. Only consider flags this class 169 * knows about in order to allow this class to work with future version of the header format. 170 */ validateHeader(int expectedType, int expectedFlags)171 public boolean validateHeader(int expectedType, int expectedFlags) { 172 return getType() == expectedType && validateHeader(expectedFlags); 173 } 174 175 /** 176 * @see Object#hashCode() 177 */ 178 @Override hashCode()179 public int hashCode() { 180 final int prime = 31; 181 int result = 1; 182 result = prime * result + ((mDataHeader == null) ? 0 : mDataHeader.hashCode()); 183 result = prime * result + mFlags; 184 result = prime * result + (int) (mRequestId ^ (mRequestId >>> 32)); 185 result = prime * result + mType; 186 return result; 187 } 188 189 /** 190 * @see Object#equals(Object) 191 */ 192 @Override equals(Object object)193 public boolean equals(Object object) { 194 if (object == this) return true; 195 if (object == null) return false; 196 if (getClass() != object.getClass()) return false; 197 198 MessageHeader other = (MessageHeader) object; 199 return (BindingsHelper.equals(mDataHeader, other.mDataHeader) 200 && mFlags == other.mFlags 201 && mRequestId == other.mRequestId 202 && mType == other.mType); 203 } 204 205 /** 206 * Set the request id on the message contained in the given buffer. 207 */ setRequestId(ByteBuffer buffer, long requestId)208 void setRequestId(ByteBuffer buffer, long requestId) { 209 assert mustHaveRequestId(buffer.getInt(FLAGS_OFFSET)); 210 buffer.putLong(REQUEST_ID_OFFSET, requestId); 211 mRequestId = requestId; 212 } 213 214 /** 215 * Returns whether a message with the given flags must have a request Id. 216 */ mustHaveRequestId(int flags)217 private static boolean mustHaveRequestId(int flags) { 218 return (flags & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG)) != 0; 219 } 220 221 /** 222 * Validate that the given {@link DataHeader} can be the data header of a message header. 223 */ validateDataHeader(DataHeader dataHeader)224 private static void validateDataHeader(DataHeader dataHeader) { 225 if (dataHeader.elementsOrVersion < SIMPLE_MESSAGE_VERSION) { 226 throw new DeserializationException("Incorrect number of fields, expecting at least " 227 + SIMPLE_MESSAGE_VERSION + ", but got: " + dataHeader.elementsOrVersion); 228 } 229 if (dataHeader.size < SIMPLE_MESSAGE_SIZE) { 230 throw new DeserializationException( 231 "Incorrect message size, expecting at least " + SIMPLE_MESSAGE_SIZE 232 + ", but got: " + dataHeader.size); 233 } 234 if (dataHeader.elementsOrVersion == SIMPLE_MESSAGE_VERSION 235 && dataHeader.size != SIMPLE_MESSAGE_SIZE) { 236 throw new DeserializationException("Incorrect message size for a message with " 237 + SIMPLE_MESSAGE_VERSION + " fields, expecting " + SIMPLE_MESSAGE_SIZE 238 + ", but got: " + dataHeader.size); 239 } 240 if (dataHeader.elementsOrVersion == MESSAGE_WITH_REQUEST_ID_VERSION 241 && dataHeader.size != MESSAGE_WITH_REQUEST_ID_SIZE) { 242 throw new DeserializationException("Incorrect message size for a message with " 243 + MESSAGE_WITH_REQUEST_ID_VERSION + " fields, expecting " 244 + MESSAGE_WITH_REQUEST_ID_SIZE + ", but got: " + dataHeader.size); 245 } 246 } 247 248 } 249