• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.usb.descriptors;
17 
18 // Framework builds and Android Studio builds use different imports for NonNull.
19 // This one for Framework builds
20 import android.annotation.NonNull;
21 // this one in the AndroidStudio project
22 // import android.support.annotation.NonNull;
23 
24 /**
25  * @hide
26  * A stream interface wrapping a byte array. Very much like a java.io.ByteArrayInputStream
27  * but with the capability to "back up" in situations where the parser discovers that a
28  * UsbDescriptor has overrun its length.
29  */
30 public final class ByteStream {
31     private static final String TAG = "ByteStream";
32 
33     /** The byte array being wrapped */
34     @NonNull
35     private final byte[] mBytes; // this is never null.
36 
37     /**
38      * The index into the byte array to be read next.
39      * This value is altered by reading data out of the stream
40      * (using either the getByte() or unpack*() methods), or alternatively
41      * by explicitly offseting the stream position with either
42      * advance() or reverse().
43      */
44     private int mIndex;
45 
46     /*
47      * This member used with resetReadCount() & getReadCount() can be used to determine how many
48      * bytes a UsbDescriptor subclass ACTUALLY reads (as opposed to what its length field says).
49      * using this info, the parser can mark a descriptor as valid or invalid and correct the stream
50      * position with advance() & reverse() to keep from "getting lost" in the descriptor stream.
51      */
52     private int mReadCount;
53 
54     /**
55      * Create a ByteStream object wrapping the specified byte array.
56      *
57      * @param bytes The byte array containing the raw descriptor information retrieved from
58      *              the USB device.
59      * @throws IllegalArgumentException
60      */
ByteStream(@onNull byte[] bytes)61     public ByteStream(@NonNull byte[] bytes) {
62         if (bytes == null) {
63             throw new IllegalArgumentException();
64         }
65         mBytes = bytes;
66     }
67 
68     /**
69      * Resets the running count of bytes read so that later we can see how much more has been read.
70      */
resetReadCount()71     public void resetReadCount() {
72         mReadCount = 0;
73     }
74 
75     /**
76      * Retrieves the running count of bytes read from the stream.
77      */
getReadCount()78     public int getReadCount() {
79         return mReadCount;
80     }
81 
82     /**
83      * @return The value of the next byte in the stream without advancing the stream.
84      * Does not affect the running count as the byte hasn't been "consumed".
85      * @throws IndexOutOfBoundsException
86      */
peekByte()87     public byte peekByte() {
88         if (available() > 0) {
89             return mBytes[mIndex + 1];
90         } else {
91             throw new IndexOutOfBoundsException();
92         }
93     }
94 
95     /**
96      * @return the next byte from the stream and advances the stream and the read count. Note
97      * that this is a signed byte (as is the case of byte in Java). The user may need to understand
98      * from context if it should be interpreted as an unsigned value.
99      * @throws IndexOutOfBoundsException
100      */
getByte()101     public byte getByte() {
102         if (available() > 0) {
103             mReadCount++;
104             return mBytes[mIndex++];
105         } else {
106             throw new IndexOutOfBoundsException();
107         }
108     }
109 
110     /**
111      * @return the next byte from the stream and advances the stream and the read count. Note
112      * that this is an unsigned byte encoded in a Java int.
113      * @throws IndexOutOfBoundsException
114      */
getUnsignedByte()115     public int getUnsignedByte() {
116         if (available() > 0) {
117             mReadCount++;
118             return mBytes[mIndex++] & 0x000000FF;
119         } else {
120             throw new IndexOutOfBoundsException();
121         }
122     }
123 
124     /**
125      * Reads 2 bytes in *little endian format* from the stream and composes a 16-bit integer.
126      * As we are storing the 2-byte value in a 4-byte integer, the upper 2 bytes are always
127      * 0, essentially making the returned value *unsigned*.
128      * @return The 16-bit integer (packed into the lower 2 bytes of an int) encoded by the
129      * next 2 bytes in the stream.
130      * @throws IndexOutOfBoundsException
131      */
unpackUsbShort()132     public int unpackUsbShort() {
133         if (available() >= 2) {
134             int b0 = getUnsignedByte();
135             int b1 = getUnsignedByte();
136             return (b1 << 8) | b0;
137         } else {
138             throw new IndexOutOfBoundsException();
139         }
140     }
141 
142     /**
143      * Reads 3 bytes in *little endian format* from the stream and composes a 24-bit integer.
144      * As we are storing the 3-byte value in a 4-byte integer, the upper byte is always
145      * 0, essentially making the returned value *unsigned*.
146      * @return The 24-bit integer (packed into the lower 3 bytes of an int) encoded by the
147      * next 3 bytes in the stream.
148      * @throws IndexOutOfBoundsException
149      */
unpackUsbTriple()150     public int unpackUsbTriple() {
151         if (available() >= 3) {
152             int b0 = getUnsignedByte();
153             int b1 = getUnsignedByte();
154             int b2 = getUnsignedByte();
155             return (b2 << 16) | (b1 << 8) | b0;
156         } else {
157             throw new IndexOutOfBoundsException();
158         }
159     }
160 
161     /**
162      * Reads 4 bytes in *little endian format* from the stream and composes a 32-bit integer.
163      * @return The 32-bit integer encoded by the next 4 bytes in the stream.
164      * @throws IndexOutOfBoundsException
165      */
unpackUsbInt()166     public int unpackUsbInt() {
167         if (available() >= 4) {
168             int b0 = getUnsignedByte();
169             int b1 = getUnsignedByte();
170             int b2 = getUnsignedByte();
171             int b3 = getUnsignedByte();
172             return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
173         } else {
174             throw new IndexOutOfBoundsException();
175         }
176     }
177     /**
178      * Advances the logical position in the stream. Affects the running count also.
179      * @param numBytes The number of bytes to advance.
180      * @throws IndexOutOfBoundsException
181      * @throws IllegalArgumentException
182      */
advance(int numBytes)183     public void advance(int numBytes) {
184         if (numBytes < 0) {
185             // Positive offsets only
186             throw new IllegalArgumentException();
187         }
188         // do arithmetic and comparison in long to ovoid potention integer overflow
189         long longNewIndex = (long) mIndex + (long) numBytes;
190         if (longNewIndex < (long) mBytes.length) {
191             mReadCount += numBytes;
192             mIndex += numBytes;
193         } else {
194             throw new IndexOutOfBoundsException();
195         }
196     }
197 
198     /**
199      * Reverse the logical position in the stream. Affects the running count also.
200      * @param numBytes The (positive) number of bytes to reverse.
201      * @throws IndexOutOfBoundsException
202      * @throws IllegalArgumentException
203      */
reverse(int numBytes)204     public void reverse(int numBytes) {
205         if (numBytes < 0) {
206             // Positive (reverse) offsets only
207             throw new IllegalArgumentException();
208         }
209         if (mIndex >= numBytes) {
210             mReadCount -= numBytes;
211             mIndex -= numBytes;
212         } else {
213             throw new IndexOutOfBoundsException();
214         }
215     }
216 
217     /**
218      * @return The number of bytes available to be read in the stream.
219      */
available()220     public int available() {
221         return mBytes.length - mIndex;
222     }
223 }
224