Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / mojo / public / java / bindings / src / org / chromium / mojo / bindings / Decoder.java
index 86a038f..efb9842 100644 (file)
@@ -4,12 +4,14 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.mojo.bindings.Interface.Proxy;
 import org.chromium.mojo.bindings.Struct.DataHeader;
 import org.chromium.mojo.system.DataPipe;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.InvalidHandle;
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.UntypedHandle;
 
 import java.nio.ByteOrder;
 import java.nio.charset.Charset;
@@ -40,10 +42,16 @@ public class Decoder {
         private final long mMaxMemory;
 
         /**
+         * The number of handles in the message.
+         */
+        private final long mNumberOfHandles;
+
+        /**
          * Constructor.
          */
-        Validator(long maxMemory) {
+        Validator(long maxMemory, int numberOfHandles) {
             mMaxMemory = maxMemory;
+            mNumberOfHandles = numberOfHandles;
         }
 
         public void claimHandle(int handle) {
@@ -51,10 +59,16 @@ public class Decoder {
                 throw new DeserializationException(
                         "Trying to access handle out of order.");
             }
+            if (handle >= mNumberOfHandles) {
+                throw new DeserializationException("Trying to access non present handle.");
+            }
             mMinNextClaimedHandle = handle + 1;
         }
 
         public void claimMemory(long start, long end) {
+            if (start % BindingsHelper.ALIGNMENT != 0) {
+                throw new DeserializationException("Incorrect starting alignment: " + start + ".");
+            }
             if (start < mMinNextMemory) {
                 throw new DeserializationException("Trying to access memory out of order.");
             }
@@ -64,10 +78,7 @@ public class Decoder {
             if (end > mMaxMemory) {
                 throw new DeserializationException("Trying to access out of range memory.");
             }
-            if (start % BindingsHelper.ALIGNMENT != 0 || end % BindingsHelper.ALIGNMENT != 0) {
-                throw new DeserializationException("Incorrect alignment.");
-            }
-            mMinNextMemory = end;
+            mMinNextMemory = BindingsHelper.align(end);
         }
     }
 
@@ -92,41 +103,60 @@ public class Decoder {
      * @param message The message to decode.
      */
     public Decoder(Message message) {
-        this(message, new Validator(message.buffer.limit()), 0);
+        this(message, new Validator(message.getData().limit(), message.getHandles().size()), 0);
     }
 
     private Decoder(Message message, Validator validator, int baseOffset) {
         mMessage = message;
-        mMessage.buffer.order(ByteOrder.nativeOrder());
+        mMessage.getData().order(ByteOrder.nativeOrder());
         mBaseOffset = baseOffset;
         mValidator = validator;
-        // Claim the memory for the header.
-        mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE);
     }
 
     /**
      * Deserializes a {@link DataHeader} at the given offset.
      */
     public DataHeader readDataHeader() {
+        // Claim the memory for the header.
+        mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE);
         int size = readInt(DataHeader.SIZE_OFFSET);
         int numFields = readInt(DataHeader.NUM_FIELDS_OFFSET);
-        // The memory for the header has already been claimed.
+        if (size < 0) {
+            throw new DeserializationException(
+                    "Negative size. Unsigned integers are not valid for java.");
+        }
+        if (numFields < 0) {
+            throw new DeserializationException(
+                    "Negative number of fields. Unsigned integers are not valid for java.");
+        }
+
+        // Claim the remaining memory.
         mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + size);
         DataHeader res = new DataHeader(size, numFields);
         return res;
     }
 
     /**
+     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+     * array where element have the given size.
+     */
+    public DataHeader readDataHeaderForPointerArray(int expectedLength) {
+        return readDataHeaderForArray(8, expectedLength);
+    }
+
+    /**
      * Deserializes a byte at the given offset.
      */
     public byte readByte(int offset) {
-        return mMessage.buffer.get(mBaseOffset + offset);
+        validateBufferSize(offset, 1);
+        return mMessage.getData().get(mBaseOffset + offset);
     }
 
     /**
      * Deserializes a boolean at the given offset, re-using any partially read byte.
      */
     public boolean readBoolean(int offset, int bit) {
+        validateBufferSize(offset, 1);
         return (readByte(offset) & (1 << bit)) != 0;
     }
 
@@ -134,45 +164,54 @@ public class Decoder {
      * Deserializes a short at the given offset.
      */
     public short readShort(int offset) {
-        return mMessage.buffer.getShort(mBaseOffset + offset);
+        validateBufferSize(offset, 2);
+        return mMessage.getData().getShort(mBaseOffset + offset);
     }
 
     /**
      * Deserializes an int at the given offset.
      */
     public int readInt(int offset) {
-        return mMessage.buffer.getInt(mBaseOffset + offset);
+        validateBufferSize(offset, 4);
+        return mMessage.getData().getInt(mBaseOffset + offset);
     }
 
     /**
      * Deserializes a float at the given offset.
      */
     public float readFloat(int offset) {
-        return mMessage.buffer.getFloat(mBaseOffset + offset);
+        validateBufferSize(offset, 4);
+        return mMessage.getData().getFloat(mBaseOffset + offset);
     }
 
     /**
      * Deserializes a long at the given offset.
      */
     public long readLong(int offset) {
-        return mMessage.buffer.getLong(mBaseOffset + offset);
+        validateBufferSize(offset, 8);
+        return mMessage.getData().getLong(mBaseOffset + offset);
     }
 
     /**
      * Deserializes a double at the given offset.
      */
     public double readDouble(int offset) {
-        return mMessage.buffer.getDouble(mBaseOffset + offset);
+        validateBufferSize(offset, 8);
+        return mMessage.getData().getDouble(mBaseOffset + offset);
     }
 
     /**
      * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content
      * of the pointer.
      */
-    public Decoder readPointer(int offset) {
+    public Decoder readPointer(int offset, boolean nullable) {
         int basePosition = mBaseOffset + offset;
         long pointerOffset = readLong(offset);
         if (pointerOffset == 0) {
+            if (!nullable) {
+                throw new DeserializationException(
+                        "Trying to decode null pointer for a non-nullable type.");
+            }
             return null;
         }
         int newPosition = (int) (basePosition + pointerOffset);
@@ -184,15 +223,15 @@ public class Decoder {
     /**
      * Deserializes an array of boolean at the given offset.
      */
-    public boolean[] readBooleans(int offset) {
-        Decoder d = readPointer(offset);
+    public boolean[] readBooleans(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
-        byte[] bytes = new byte[si.numFields + 7 / BindingsHelper.ALIGNMENT];
-        d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE);
-        d.mMessage.buffer.get(bytes);
+        DataHeader si = d.readDataHeaderForBooleanArray(expectedLength);
+        byte[] bytes = new byte[(si.numFields + 7) / BindingsHelper.ALIGNMENT];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().get(bytes);
         boolean[] result = new boolean[si.numFields];
         for (int i = 0; i < bytes.length; ++i) {
             for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
@@ -208,154 +247,176 @@ public class Decoder {
     /**
      * Deserializes an array of bytes at the given offset.
      */
-    public byte[] readBytes(int offset) {
-        Decoder d = readPointer(offset);
+    public byte[] readBytes(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(1, expectedLength);
         byte[] result = new byte[si.numFields];
-        d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE);
-        d.mMessage.buffer.get(result);
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().get(result);
         return result;
     }
 
     /**
      * Deserializes an array of shorts at the given offset.
      */
-    public short[] readShorts(int offset) {
-        Decoder d = readPointer(offset);
+    public short[] readShorts(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(2, expectedLength);
         short[] result = new short[si.numFields];
-        d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE);
-        d.mMessage.buffer.asShortBuffer().get(result);
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asShortBuffer().get(result);
         return result;
     }
 
     /**
      * Deserializes an array of ints at the given offset.
      */
-    public int[] readInts(int offset) {
-        Decoder d = readPointer(offset);
+    public int[] readInts(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         int[] result = new int[si.numFields];
-        d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE);
-        d.mMessage.buffer.asIntBuffer().get(result);
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asIntBuffer().get(result);
         return result;
     }
 
     /**
      * Deserializes an array of floats at the given offset.
      */
-    public float[] readFloats(int offset) {
-        Decoder d = readPointer(offset);
+    public float[] readFloats(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         float[] result = new float[si.numFields];
-        d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE);
-        d.mMessage.buffer.asFloatBuffer().get(result);
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asFloatBuffer().get(result);
         return result;
     }
 
     /**
      * Deserializes an array of longs at the given offset.
      */
-    public long[] readLongs(int offset) {
-        Decoder d = readPointer(offset);
+    public long[] readLongs(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(8, expectedLength);
         long[] result = new long[si.numFields];
-        d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE);
-        d.mMessage.buffer.asLongBuffer().get(result);
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asLongBuffer().get(result);
         return result;
     }
 
     /**
      * Deserializes an array of doubles at the given offset.
      */
-    public double[] readDoubles(int offset) {
-        Decoder d = readPointer(offset);
+    public double[] readDoubles(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(8, expectedLength);
         double[] result = new double[si.numFields];
-        d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE);
-        d.mMessage.buffer.asDoubleBuffer().get(result);
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().asDoubleBuffer().get(result);
         return result;
     }
 
     /**
      * Deserializes an |Handle| at the given offset.
      */
-    public Handle readHandle(int offset) {
+    public Handle readHandle(int offset, boolean nullable) {
         int index = readInt(offset);
         if (index == -1) {
+            if (!nullable) {
+                throw new DeserializationException(
+                        "Trying to decode an invalid handle for a non-nullable type.");
+            }
             return InvalidHandle.INSTANCE;
         }
         mValidator.claimHandle(index);
-        return mMessage.handles.get(index);
+        return mMessage.getHandles().get(index);
+    }
+
+    /**
+     * Deserializes an |UntypedHandle| at the given offset.
+     */
+    public UntypedHandle readUntypedHandle(int offset, boolean nullable) {
+        return readHandle(offset, nullable).toUntypedHandle();
     }
 
     /**
      * Deserializes a |ConsumerHandle| at the given offset.
      */
-    public DataPipe.ConsumerHandle readConsumerHandle(int offset) {
-        return readHandle(offset).toUntypedHandle().toDataPipeConsumerHandle();
+    public DataPipe.ConsumerHandle readConsumerHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toDataPipeConsumerHandle();
     }
 
     /**
      * Deserializes a |ProducerHandle| at the given offset.
      */
-    public DataPipe.ProducerHandle readProducerHandle(int offset) {
-        return readHandle(offset).toUntypedHandle().toDataPipeProducerHandle();
+    public DataPipe.ProducerHandle readProducerHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toDataPipeProducerHandle();
     }
 
     /**
      * Deserializes a |MessagePipeHandle| at the given offset.
      */
-    public MessagePipeHandle readMessagePipeHandle(int offset) {
-        return readHandle(offset).toUntypedHandle().toMessagePipeHandle();
+    public MessagePipeHandle readMessagePipeHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toMessagePipeHandle();
     }
 
     /**
      * Deserializes a |SharedBufferHandle| at the given offset.
      */
-    public SharedBufferHandle readSharedBufferHandle(int offset) {
-        return readHandle(offset).toUntypedHandle().toSharedBufferHandle();
+    public SharedBufferHandle readSharedBufferHandle(int offset, boolean nullable) {
+        return readUntypedHandle(offset, nullable).toSharedBufferHandle();
     }
 
     /**
-     * Deserializes a |ServiceHandle| at the given offset.
+     * Deserializes an interface at the given offset.
+     *
+     * @return a proxy to the service.
      */
-    public <S extends Interface> S readServiceInterface(int offset, Object builder) {
-        // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented.
-        throw new UnsupportedOperationException("Unimplemented operation");
+    public <P extends Proxy> P readServiceInterface(int offset, boolean nullable,
+            Interface.Manager<?, P> manager) {
+        MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+        if (!handle.isValid()) {
+            return null;
+        }
+        return manager.attachProxy(handle);
     }
 
     /**
      * Deserializes a |InterfaceRequest| at the given offset.
      */
-    public <S extends Interface> InterfaceRequest<S> readInterfaceRequest(int offset) {
-        // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented.
-        throw new UnsupportedOperationException("Unimplemented operation");
+    public <I extends Interface> InterfaceRequest<I> readInterfaceRequest(int offset,
+            boolean nullable) {
+        MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+        if (handle == null) {
+            return null;
+        }
+        return new InterfaceRequest<I>(handle);
     }
 
     /**
      * Deserializes a string at the given offset.
      */
-    public String readString(int offset) {
-        byte[] bytes = readBytes(offset);
+    public String readString(int offset, boolean nullable) {
+        final int arrayNullability = nullable ? BindingsHelper.ARRAY_NULLABLE : 0;
+        byte[] bytes = readBytes(offset, arrayNullability, BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
         if (bytes == null) {
             return null;
         }
@@ -365,16 +426,36 @@ public class Decoder {
     /**
      * Deserializes an array of |Handle| at the given offset.
      */
-    public Handle[] readHandles(int offset) {
-        Decoder d = readPointer(offset);
+    public Handle[] readHandles(int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         Handle[] result = new Handle[si.numFields];
         for (int i = 0; i < result.length; ++i) {
             result[i] = d.readHandle(
-                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
+        }
+        return result;
+    }
+
+    /**
+     * Deserializes an array of |UntypedHandle| at the given offset.
+     */
+    public UntypedHandle[] readUntypedHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        UntypedHandle[] result = new UntypedHandle[si.numFields];
+        for (int i = 0; i < result.length; ++i) {
+            result[i] = d.readUntypedHandle(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
         }
         return result;
     }
@@ -382,16 +463,18 @@ public class Decoder {
     /**
      * Deserializes an array of |ConsumerHandle| at the given offset.
      */
-    public DataPipe.ConsumerHandle[] readConsumerHandles(int offset) {
-        Decoder d = readPointer(offset);
+    public DataPipe.ConsumerHandle[] readConsumerHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.numFields];
         for (int i = 0; i < result.length; ++i) {
             result[i] = d.readConsumerHandle(
-                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
         }
         return result;
     }
@@ -399,16 +482,18 @@ public class Decoder {
     /**
      * Deserializes an array of |ProducerHandle| at the given offset.
      */
-    public DataPipe.ProducerHandle[] readProducerHandles(int offset) {
-        Decoder d = readPointer(offset);
+    public DataPipe.ProducerHandle[] readProducerHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.numFields];
         for (int i = 0; i < result.length; ++i) {
             result[i] = d.readProducerHandle(
-                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
         }
         return result;
 
@@ -417,16 +502,18 @@ public class Decoder {
     /**
      * Deserializes an array of |MessagePipeHandle| at the given offset.
      */
-    public MessagePipeHandle[] readMessagePipeHandles(int offset) {
-        Decoder d = readPointer(offset);
+    public MessagePipeHandle[] readMessagePipeHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         MessagePipeHandle[] result = new MessagePipeHandle[si.numFields];
         for (int i = 0; i < result.length; ++i) {
             result[i] = d.readMessagePipeHandle(
-                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
         }
         return result;
 
@@ -435,16 +522,18 @@ public class Decoder {
     /**
      * Deserializes an array of |SharedBufferHandle| at the given offset.
      */
-    public SharedBufferHandle[] readSharedBufferHandles(int offset) {
-        Decoder d = readPointer(offset);
+    public SharedBufferHandle[] readSharedBufferHandles(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         SharedBufferHandle[] result = new SharedBufferHandle[si.numFields];
         for (int i = 0; i < result.length; ++i) {
             result[i] = d.readSharedBufferHandle(
-                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
         }
         return result;
 
@@ -453,25 +542,42 @@ public class Decoder {
     /**
      * Deserializes an array of |ServiceHandle| at the given offset.
      */
-    public <S extends Interface> S[] readServiceInterfaces(int offset, Object builder) {
-        // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented.
-        throw new UnsupportedOperationException("Unimplemented operation");
+    public <S extends Interface, P extends Proxy> S[] readServiceInterfaces(
+            int offset, int arrayNullability, int expectedLength, Interface.Manager<S, P> manager) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+        if (d == null) {
+            return null;
+        }
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+        S[] result = manager.buildArray(si.numFields);
+        for (int i = 0; i < result.length; ++i) {
+            // This cast is necessary because java 6 doesn't handle wildcard correctly when using
+            // Manager<S, ? extends S>
+            @SuppressWarnings("unchecked")
+            S value = (S) d.readServiceInterface(
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability), manager);
+            result[i] = value;
+        }
+        return result;
     }
 
     /**
      * Deserializes an array of |InterfaceRequest| at the given offset.
      */
-    public <S extends Interface> InterfaceRequest<S>[] readInterfaceRequests(int offset) {
-        Decoder d = readPointer(offset);
+    public <I extends Interface> InterfaceRequest<I>[] readInterfaceRequests(
+            int offset, int arrayNullability, int expectedLength) {
+        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
         }
-        DataHeader si = d.readDataHeader();
+        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
         @SuppressWarnings("unchecked")
-        InterfaceRequest<S>[] result = new InterfaceRequest[si.numFields];
+        InterfaceRequest<I>[] result = new InterfaceRequest[si.numFields];
         for (int i = 0; i < result.length; ++i) {
             result[i] = d.readInterfaceRequest(
-                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
+                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability));
         }
         return result;
     }
@@ -483,4 +589,42 @@ public class Decoder {
         return new Decoder(mMessage, mValidator, offset);
     }
 
+    /**
+     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+     * array of booleans.
+     */
+    private DataHeader readDataHeaderForBooleanArray(int expectedLength) {
+        DataHeader dataHeader = readDataHeader();
+        if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.numFields + 7) / 8) {
+            throw new DeserializationException("Array header is incorrect.");
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+                && dataHeader.numFields != expectedLength) {
+            throw new DeserializationException("Incorrect array length. Expected: " +
+                    expectedLength + ", but got: " + dataHeader.numFields + ".");
+        }
+        return dataHeader;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} of an array at the given offset.
+     */
+    private DataHeader readDataHeaderForArray(long elementSize, int expectedLength) {
+        DataHeader dataHeader = readDataHeader();
+        if (dataHeader.size < (DataHeader.HEADER_SIZE + elementSize * dataHeader.numFields)) {
+            throw new DeserializationException("Array header is incorrect.");
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+                && dataHeader.numFields != expectedLength) {
+            throw new DeserializationException("Incorrect array length. Expected: " +
+                    expectedLength + ", but got: " + dataHeader.numFields + ".");
+        }
+        return dataHeader;
+    }
+
+    private void validateBufferSize(int offset, int size) {
+        if (mMessage.getData().limit() < offset + size) {
+            throw new DeserializationException("Buffer is smaller than expected.");
+        }
+    }
 }