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