• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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