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
5package org.chromium.mojo.bindings;
6
7import org.chromium.mojo.bindings.Interface.Proxy;
8import org.chromium.mojo.system.DataPipe;
9import org.chromium.mojo.system.Handle;
10import org.chromium.mojo.system.InvalidHandle;
11import org.chromium.mojo.system.MessagePipeHandle;
12import org.chromium.mojo.system.SharedBufferHandle;
13import org.chromium.mojo.system.UntypedHandle;
14
15import java.nio.ByteOrder;
16import java.nio.charset.Charset;
17
18/**
19 * A Decoder is a helper class for deserializing a mojo struct. It enables deserialization of basic
20 * types from a {@link Message} object at a given offset into it's byte buffer.
21 */
22public class Decoder {
23
24    /**
25     * Helper class to validate the decoded message.
26     */
27    static final class Validator {
28
29        /**
30         * Minimal value for the next handle to deserialize.
31         */
32        private int mMinNextClaimedHandle = 0;
33        /**
34         * Minimal value of the start of the next memory to claim.
35         */
36        private long mMinNextMemory = 0;
37
38        /**
39         * The maximal memory accessible.
40         */
41        private final long mMaxMemory;
42
43        /**
44         * The number of handles in the message.
45         */
46        private final long mNumberOfHandles;
47
48        /**
49         * Constructor.
50         */
51        Validator(long maxMemory, int numberOfHandles) {
52            mMaxMemory = maxMemory;
53            mNumberOfHandles = numberOfHandles;
54        }
55
56        public void claimHandle(int handle) {
57            if (handle < mMinNextClaimedHandle) {
58                throw new DeserializationException(
59                        "Trying to access handle out of order.");
60            }
61            if (handle >= mNumberOfHandles) {
62                throw new DeserializationException("Trying to access non present handle.");
63            }
64            mMinNextClaimedHandle = handle + 1;
65        }
66
67        public void claimMemory(long start, long end) {
68            if (start % BindingsHelper.ALIGNMENT != 0) {
69                throw new DeserializationException("Incorrect starting alignment: " + start + ".");
70            }
71            if (start < mMinNextMemory) {
72                throw new DeserializationException("Trying to access memory out of order.");
73            }
74            if (end < start) {
75                throw new DeserializationException("Incorrect memory range.");
76            }
77            if (end > mMaxMemory) {
78                throw new DeserializationException("Trying to access out of range memory.");
79            }
80            mMinNextMemory = BindingsHelper.align(end);
81        }
82    }
83
84    /**
85     * The message to deserialize from.
86     */
87    private final Message mMessage;
88
89    /**
90     * The base offset in the byte buffer.
91     */
92    private final int mBaseOffset;
93
94    /**
95     * Validator for the decoded message.
96     */
97    private final Validator mValidator;
98
99    /**
100     * Constructor.
101     *
102     * @param message The message to decode.
103     */
104    public Decoder(Message message) {
105        this(message, new Validator(message.getData().limit(), message.getHandles().size()), 0);
106    }
107
108    private Decoder(Message message, Validator validator, int baseOffset) {
109        mMessage = message;
110        mMessage.getData().order(ByteOrder.LITTLE_ENDIAN);
111        mBaseOffset = baseOffset;
112        mValidator = validator;
113    }
114
115    /**
116     * Deserializes a {@link DataHeader} at the current position.
117     */
118    public DataHeader readDataHeader() {
119        // Claim the memory for the header.
120        mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE);
121        DataHeader result = readDataHeaderAtOffset(0, false);
122        // Claim the rest of the memory.
123        mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + result.size);
124        return result;
125    }
126
127    /**
128     * Deserializes a {@link DataHeader} for an union at the given offset.
129     */
130    public DataHeader readDataHeaderForUnion(int offset) {
131        DataHeader result = readDataHeaderAtOffset(offset, true);
132        if (result.size == 0) {
133            if (result.elementsOrVersion != 0) {
134                throw new DeserializationException(
135                        "Unexpected version tag for a null union. Expecting 0, found: "
136                        + result.elementsOrVersion);
137            }
138        } else if (result.size != BindingsHelper.UNION_SIZE) {
139            throw new DeserializationException(
140                    "Unexpected size of an union. The size must be 0 for a null union, or 16 for "
141                    + "a non-null union.");
142        }
143        return result;
144    }
145
146    /**
147     * @returns a decoder suitable to decode an union defined as the root object of a message.
148     */
149    public Decoder decoderForSerializedUnion() {
150        mValidator.claimMemory(0, BindingsHelper.UNION_SIZE);
151        return this;
152    }
153
154    /**
155     * Deserializes a {@link DataHeader} at the given offset.
156     */
157    private DataHeader readDataHeaderAtOffset(int offset, boolean isUnion) {
158        int size = readInt(offset + DataHeader.SIZE_OFFSET);
159        int elementsOrVersion = readInt(offset + DataHeader.ELEMENTS_OR_VERSION_OFFSET);
160        if (size < 0) {
161            throw new DeserializationException(
162                    "Negative size. Unsigned integers are not valid for java.");
163        }
164        if (elementsOrVersion < 0 && (!isUnion || elementsOrVersion != -1)) {
165            throw new DeserializationException(
166                    "Negative elements or version. Unsigned integers are not valid for java.");
167        }
168
169        return new DataHeader(size, elementsOrVersion);
170    }
171
172    public DataHeader readAndValidateDataHeader(DataHeader[] versionArray) {
173        DataHeader header = readDataHeader();
174        int maxVersionIndex = versionArray.length - 1;
175        if (header.elementsOrVersion <= versionArray[maxVersionIndex].elementsOrVersion) {
176            DataHeader referenceHeader = null;
177            for (int index = maxVersionIndex; index >= 0; index--) {
178                DataHeader dataHeader = versionArray[index];
179                if (header.elementsOrVersion >= dataHeader.elementsOrVersion) {
180                    referenceHeader = dataHeader;
181                    break;
182                }
183            }
184            if (referenceHeader == null || referenceHeader.size != header.size) {
185                throw new DeserializationException(
186                        "Header doesn't correspond to any known version.");
187            }
188        } else {
189            if (header.size < versionArray[maxVersionIndex].size) {
190                throw new DeserializationException("Message newer than the last known version"
191                        + " cannot be shorter than required by the last known version.");
192            }
193        }
194        return header;
195    }
196
197    /**
198     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
199     * array where elements are pointers.
200     */
201    public DataHeader readDataHeaderForPointerArray(int expectedLength) {
202        return readDataHeaderForArray(BindingsHelper.POINTER_SIZE, expectedLength);
203    }
204
205    /**
206     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
207     * array where elements are unions.
208     */
209    public DataHeader readDataHeaderForUnionArray(int expectedLength) {
210        return readDataHeaderForArray(BindingsHelper.UNION_SIZE, expectedLength);
211    }
212
213    /**
214     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for a map.
215     */
216    public void readDataHeaderForMap() {
217        DataHeader si = readDataHeader();
218        if (si.size != BindingsHelper.MAP_STRUCT_HEADER.size) {
219            throw new DeserializationException(
220                    "Incorrect header for map. The size is incorrect.");
221        }
222        if (si.elementsOrVersion != BindingsHelper.MAP_STRUCT_HEADER.elementsOrVersion) {
223            throw new DeserializationException(
224                    "Incorrect header for map. The version is incorrect.");
225        }
226    }
227
228    /**
229     * Deserializes a byte at the given offset.
230     */
231    public byte readByte(int offset) {
232        validateBufferSize(offset, 1);
233        return mMessage.getData().get(mBaseOffset + offset);
234    }
235
236    /**
237     * Deserializes a boolean at the given offset, re-using any partially read byte.
238     */
239    public boolean readBoolean(int offset, int bit) {
240        validateBufferSize(offset, 1);
241        return (readByte(offset) & (1 << bit)) != 0;
242    }
243
244    /**
245     * Deserializes a short at the given offset.
246     */
247    public short readShort(int offset) {
248        validateBufferSize(offset, 2);
249        return mMessage.getData().getShort(mBaseOffset + offset);
250    }
251
252    /**
253     * Deserializes an int at the given offset.
254     */
255    public int readInt(int offset) {
256        validateBufferSize(offset, 4);
257        return mMessage.getData().getInt(mBaseOffset + offset);
258    }
259
260    /**
261     * Deserializes a float at the given offset.
262     */
263    public float readFloat(int offset) {
264        validateBufferSize(offset, 4);
265        return mMessage.getData().getFloat(mBaseOffset + offset);
266    }
267
268    /**
269     * Deserializes a long at the given offset.
270     */
271    public long readLong(int offset) {
272        validateBufferSize(offset, 8);
273        return mMessage.getData().getLong(mBaseOffset + offset);
274    }
275
276    /**
277     * Deserializes a double at the given offset.
278     */
279    public double readDouble(int offset) {
280        validateBufferSize(offset, 8);
281        return mMessage.getData().getDouble(mBaseOffset + offset);
282    }
283
284    /**
285     * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content
286     * of the pointer.
287     */
288    public Decoder readPointer(int offset, boolean nullable) {
289        int basePosition = mBaseOffset + offset;
290        long pointerOffset = readLong(offset);
291        if (pointerOffset == 0) {
292            if (!nullable) {
293                throw new DeserializationException(
294                        "Trying to decode null pointer for a non-nullable type.");
295            }
296            return null;
297        }
298        int newPosition = (int) (basePosition + pointerOffset);
299        // The method |getDecoderAtPosition| will validate that the pointer address is valid.
300        return getDecoderAtPosition(newPosition);
301
302    }
303
304    /**
305     * Deserializes an array of boolean at the given offset.
306     */
307    public boolean[] readBooleans(int offset, int arrayNullability, int expectedLength) {
308        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
309        if (d == null) {
310            return null;
311        }
312        DataHeader si = d.readDataHeaderForBooleanArray(expectedLength);
313        byte[] bytes = new byte[(si.elementsOrVersion + 7) / BindingsHelper.ALIGNMENT];
314        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
315        d.mMessage.getData().get(bytes);
316        boolean[] result = new boolean[si.elementsOrVersion];
317        for (int i = 0; i < bytes.length; ++i) {
318            for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
319                int booleanIndex = i * BindingsHelper.ALIGNMENT + j;
320                if (booleanIndex < result.length) {
321                    result[booleanIndex] = (bytes[i] & (1 << j)) != 0;
322                }
323            }
324        }
325        return result;
326    }
327
328    /**
329     * Deserializes an array of bytes at the given offset.
330     */
331    public byte[] readBytes(int offset, int arrayNullability, int expectedLength) {
332        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
333        if (d == null) {
334            return null;
335        }
336        DataHeader si = d.readDataHeaderForArray(1, expectedLength);
337        byte[] result = new byte[si.elementsOrVersion];
338        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
339        d.mMessage.getData().get(result);
340        return result;
341    }
342
343    /**
344     * Deserializes an array of shorts at the given offset.
345     */
346    public short[] readShorts(int offset, int arrayNullability, int expectedLength) {
347        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
348        if (d == null) {
349            return null;
350        }
351        DataHeader si = d.readDataHeaderForArray(2, expectedLength);
352        short[] result = new short[si.elementsOrVersion];
353        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
354        d.mMessage.getData().asShortBuffer().get(result);
355        return result;
356    }
357
358    /**
359     * Deserializes an array of ints at the given offset.
360     */
361    public int[] readInts(int offset, int arrayNullability, int expectedLength) {
362        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
363        if (d == null) {
364            return null;
365        }
366        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
367        int[] result = new int[si.elementsOrVersion];
368        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
369        d.mMessage.getData().asIntBuffer().get(result);
370        return result;
371    }
372
373    /**
374     * Deserializes an array of floats at the given offset.
375     */
376    public float[] readFloats(int offset, int arrayNullability, int expectedLength) {
377        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
378        if (d == null) {
379            return null;
380        }
381        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
382        float[] result = new float[si.elementsOrVersion];
383        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
384        d.mMessage.getData().asFloatBuffer().get(result);
385        return result;
386    }
387
388    /**
389     * Deserializes an array of longs at the given offset.
390     */
391    public long[] readLongs(int offset, int arrayNullability, int expectedLength) {
392        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
393        if (d == null) {
394            return null;
395        }
396        DataHeader si = d.readDataHeaderForArray(8, expectedLength);
397        long[] result = new long[si.elementsOrVersion];
398        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
399        d.mMessage.getData().asLongBuffer().get(result);
400        return result;
401    }
402
403    /**
404     * Deserializes an array of doubles at the given offset.
405     */
406    public double[] readDoubles(int offset, int arrayNullability, int expectedLength) {
407        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
408        if (d == null) {
409            return null;
410        }
411        DataHeader si = d.readDataHeaderForArray(8, expectedLength);
412        double[] result = new double[si.elementsOrVersion];
413        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
414        d.mMessage.getData().asDoubleBuffer().get(result);
415        return result;
416    }
417
418    /**
419     * Deserializes an |Handle| at the given offset.
420     */
421    public Handle readHandle(int offset, boolean nullable) {
422        int index = readInt(offset);
423        if (index == -1) {
424            if (!nullable) {
425                throw new DeserializationException(
426                        "Trying to decode an invalid handle for a non-nullable type.");
427            }
428            return InvalidHandle.INSTANCE;
429        }
430        mValidator.claimHandle(index);
431        return mMessage.getHandles().get(index);
432    }
433
434    /**
435     * Deserializes an |UntypedHandle| at the given offset.
436     */
437    public UntypedHandle readUntypedHandle(int offset, boolean nullable) {
438        return readHandle(offset, nullable).toUntypedHandle();
439    }
440
441    /**
442     * Deserializes a |ConsumerHandle| at the given offset.
443     */
444    public DataPipe.ConsumerHandle readConsumerHandle(int offset, boolean nullable) {
445        return readUntypedHandle(offset, nullable).toDataPipeConsumerHandle();
446    }
447
448    /**
449     * Deserializes a |ProducerHandle| at the given offset.
450     */
451    public DataPipe.ProducerHandle readProducerHandle(int offset, boolean nullable) {
452        return readUntypedHandle(offset, nullable).toDataPipeProducerHandle();
453    }
454
455    /**
456     * Deserializes a |MessagePipeHandle| at the given offset.
457     */
458    public MessagePipeHandle readMessagePipeHandle(int offset, boolean nullable) {
459        return readUntypedHandle(offset, nullable).toMessagePipeHandle();
460    }
461
462    /**
463     * Deserializes a |SharedBufferHandle| at the given offset.
464     */
465    public SharedBufferHandle readSharedBufferHandle(int offset, boolean nullable) {
466        return readUntypedHandle(offset, nullable).toSharedBufferHandle();
467    }
468
469    /**
470     * Deserializes an interface at the given offset.
471     *
472     * @return a proxy to the service.
473     */
474    public <P extends Proxy> P readServiceInterface(int offset, boolean nullable,
475            Interface.Manager<?, P> manager) {
476        MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
477        if (!handle.isValid()) {
478            return null;
479        }
480        int version = readInt(offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
481        return manager.attachProxy(handle, version);
482    }
483
484    /**
485     * Deserializes a |InterfaceRequest| at the given offset.
486     */
487    public <I extends Interface> InterfaceRequest<I> readInterfaceRequest(int offset,
488            boolean nullable) {
489        MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
490        if (handle == null) {
491            return null;
492        }
493        return new InterfaceRequest<I>(handle);
494    }
495
496    /**
497     * Deserializes an associated interface at the given offset. Not yet supported.
498     */
499    public AssociatedInterfaceNotSupported readAssociatedServiceInterfaceNotSupported(int offset,
500            boolean nullable) {
501        return null;
502    }
503
504    /**
505     * Deserializes an associated interface request at the given offset. Not yet supported.
506     */
507    public AssociatedInterfaceRequestNotSupported readAssociatedInterfaceRequestNotSupported(
508            int offset, boolean nullable) {
509        return null;
510    }
511
512    /**
513     * Deserializes a string at the given offset.
514     */
515    public String readString(int offset, boolean nullable) {
516        final int arrayNullability = nullable ? BindingsHelper.ARRAY_NULLABLE : 0;
517        byte[] bytes = readBytes(offset, arrayNullability, BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
518        if (bytes == null) {
519            return null;
520        }
521        return new String(bytes, Charset.forName("utf8"));
522    }
523
524    /**
525     * Deserializes an array of |Handle| at the given offset.
526     */
527    public Handle[] readHandles(int offset, int arrayNullability, int expectedLength) {
528        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
529        if (d == null) {
530            return null;
531        }
532        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
533        Handle[] result = new Handle[si.elementsOrVersion];
534        for (int i = 0; i < result.length; ++i) {
535            result[i] = d.readHandle(
536                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
537                    BindingsHelper.isElementNullable(arrayNullability));
538        }
539        return result;
540    }
541
542    /**
543     * Deserializes an array of |UntypedHandle| at the given offset.
544     */
545    public UntypedHandle[] readUntypedHandles(
546            int offset, int arrayNullability, int expectedLength) {
547        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
548        if (d == null) {
549            return null;
550        }
551        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
552        UntypedHandle[] result = new UntypedHandle[si.elementsOrVersion];
553        for (int i = 0; i < result.length; ++i) {
554            result[i] = d.readUntypedHandle(
555                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
556                    BindingsHelper.isElementNullable(arrayNullability));
557        }
558        return result;
559    }
560
561    /**
562     * Deserializes an array of |ConsumerHandle| at the given offset.
563     */
564    public DataPipe.ConsumerHandle[] readConsumerHandles(
565            int offset, int arrayNullability, int expectedLength) {
566        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
567        if (d == null) {
568            return null;
569        }
570        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
571        DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.elementsOrVersion];
572        for (int i = 0; i < result.length; ++i) {
573            result[i] = d.readConsumerHandle(
574                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
575                    BindingsHelper.isElementNullable(arrayNullability));
576        }
577        return result;
578    }
579
580    /**
581     * Deserializes an array of |ProducerHandle| at the given offset.
582     */
583    public DataPipe.ProducerHandle[] readProducerHandles(
584            int offset, int arrayNullability, int expectedLength) {
585        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
586        if (d == null) {
587            return null;
588        }
589        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
590        DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.elementsOrVersion];
591        for (int i = 0; i < result.length; ++i) {
592            result[i] = d.readProducerHandle(
593                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
594                    BindingsHelper.isElementNullable(arrayNullability));
595        }
596        return result;
597
598    }
599
600    /**
601     * Deserializes an array of |MessagePipeHandle| at the given offset.
602     */
603    public MessagePipeHandle[] readMessagePipeHandles(
604            int offset, int arrayNullability, int expectedLength) {
605        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
606        if (d == null) {
607            return null;
608        }
609        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
610        MessagePipeHandle[] result = new MessagePipeHandle[si.elementsOrVersion];
611        for (int i = 0; i < result.length; ++i) {
612            result[i] = d.readMessagePipeHandle(
613                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
614                    BindingsHelper.isElementNullable(arrayNullability));
615        }
616        return result;
617
618    }
619
620    /**
621     * Deserializes an array of |SharedBufferHandle| at the given offset.
622     */
623    public SharedBufferHandle[] readSharedBufferHandles(
624            int offset, int arrayNullability, int expectedLength) {
625        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
626        if (d == null) {
627            return null;
628        }
629        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
630        SharedBufferHandle[] result = new SharedBufferHandle[si.elementsOrVersion];
631        for (int i = 0; i < result.length; ++i) {
632            result[i] = d.readSharedBufferHandle(
633                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
634                    BindingsHelper.isElementNullable(arrayNullability));
635        }
636        return result;
637
638    }
639
640    /**
641     * Deserializes an array of |ServiceHandle| at the given offset.
642     */
643    public <S extends Interface, P extends Proxy> S[] readServiceInterfaces(
644            int offset, int arrayNullability, int expectedLength, Interface.Manager<S, P> manager) {
645        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
646        if (d == null) {
647            return null;
648        }
649        DataHeader si =
650                d.readDataHeaderForArray(BindingsHelper.SERIALIZED_INTERFACE_SIZE, expectedLength);
651        S[] result = manager.buildArray(si.elementsOrVersion);
652        for (int i = 0; i < result.length; ++i) {
653            // This cast is necessary because java 6 doesn't handle wildcard correctly when using
654            // Manager<S, ? extends S>
655            @SuppressWarnings("unchecked")
656            S value = (S) d.readServiceInterface(
657                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_INTERFACE_SIZE * i,
658                    BindingsHelper.isElementNullable(arrayNullability), manager);
659            result[i] = value;
660        }
661        return result;
662    }
663
664    /**
665     * Deserializes an array of |InterfaceRequest| at the given offset.
666     */
667    public <I extends Interface> InterfaceRequest<I>[] readInterfaceRequests(
668            int offset, int arrayNullability, int expectedLength) {
669        Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
670        if (d == null) {
671            return null;
672        }
673        DataHeader si = d.readDataHeaderForArray(4, expectedLength);
674        @SuppressWarnings("unchecked")
675        InterfaceRequest<I>[] result = new InterfaceRequest[si.elementsOrVersion];
676        for (int i = 0; i < result.length; ++i) {
677            result[i] = d.readInterfaceRequest(
678                    DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
679                    BindingsHelper.isElementNullable(arrayNullability));
680        }
681        return result;
682    }
683
684    /**
685     * Deserializes an array of associated interfaces at the given offset. Not yet supported.
686     */
687    public AssociatedInterfaceNotSupported[] readAssociatedServiceInterfaceNotSupporteds(
688            int offset, int arrayNullability, int expectedLength) {
689        return null;
690    }
691
692    /**
693     * Deserializes an array of associated interface requests at the given offset. Not yet
694     * supported.
695     */
696    public AssociatedInterfaceRequestNotSupported[] readAssociatedInterfaceRequestNotSupporteds(
697            int offset, int arrayNullability, int expectedLength) {
698        return null;
699    }
700
701    /**
702     * Returns a view of this decoder at the offset |offset|.
703     */
704    private Decoder getDecoderAtPosition(int offset) {
705        return new Decoder(mMessage, mValidator, offset);
706    }
707
708    /**
709     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
710     * array of booleans.
711     */
712    private DataHeader readDataHeaderForBooleanArray(int expectedLength) {
713        DataHeader dataHeader = readDataHeader();
714        if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.elementsOrVersion + 7) / 8) {
715            throw new DeserializationException("Array header is incorrect.");
716        }
717        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
718                && dataHeader.elementsOrVersion != expectedLength) {
719            throw new DeserializationException("Incorrect array length. Expected: " + expectedLength
720                    + ", but got: " + dataHeader.elementsOrVersion + ".");
721        }
722        return dataHeader;
723    }
724
725    /**
726     * Deserializes a {@link DataHeader} of an array at the given offset.
727     */
728    private DataHeader readDataHeaderForArray(long elementSize, int expectedLength) {
729        DataHeader dataHeader = readDataHeader();
730        if (dataHeader.size
731                < (DataHeader.HEADER_SIZE + elementSize * dataHeader.elementsOrVersion)) {
732            throw new DeserializationException("Array header is incorrect.");
733        }
734        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
735                && dataHeader.elementsOrVersion != expectedLength) {
736            throw new DeserializationException("Incorrect array length. Expected: " + expectedLength
737                    + ", but got: " + dataHeader.elementsOrVersion + ".");
738        }
739        return dataHeader;
740    }
741
742    private void validateBufferSize(int offset, int size) {
743        if (mMessage.getData().limit() < offset + size) {
744            throw new DeserializationException("Buffer is smaller than expected.");
745        }
746    }
747}
748