| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.server.usb.descriptors; |
| |
| // Framework builds and Android Studio builds use different imports for NonNull. |
| // This one for Framework builds |
| import android.annotation.NonNull; |
| // this one in the AndroidStudio project |
| // import android.support.annotation.NonNull; |
| |
| /** |
| * @hide |
| * A stream interface wrapping a byte array. Very much like a java.io.ByteArrayInputStream |
| * but with the capability to "back up" in situations where the parser discovers that a |
| * UsbDescriptor has overrun its length. |
| */ |
| public final class ByteStream { |
| private static final String TAG = "ByteStream"; |
| |
| /** The byte array being wrapped */ |
| @NonNull |
| private final byte[] mBytes; // this is never null. |
| |
| /** |
| * The index into the byte array to be read next. |
| * This value is altered by reading data out of the stream |
| * (using either the getByte() or unpack*() methods), or alternatively |
| * by explicitly offseting the stream position with either |
| * advance() or reverse(). |
| */ |
| private int mIndex; |
| |
| /* |
| * This member used with resetReadCount() & getReadCount() can be used to determine how many |
| * bytes a UsbDescriptor subclass ACTUALLY reads (as opposed to what its length field says). |
| * using this info, the parser can mark a descriptor as valid or invalid and correct the stream |
| * position with advance() & reverse() to keep from "getting lost" in the descriptor stream. |
| */ |
| private int mReadCount; |
| |
| /** |
| * Create a ByteStream object wrapping the specified byte array. |
| * |
| * @param bytes The byte array containing the raw descriptor information retrieved from |
| * the USB device. |
| * @throws IllegalArgumentException |
| */ |
| public ByteStream(@NonNull byte[] bytes) { |
| if (bytes == null) { |
| throw new IllegalArgumentException(); |
| } |
| mBytes = bytes; |
| } |
| |
| /** |
| * Resets the running count of bytes read so that later we can see how much more has been read. |
| */ |
| public void resetReadCount() { |
| mReadCount = 0; |
| } |
| |
| /** |
| * Retrieves the running count of bytes read from the stream. |
| */ |
| public int getReadCount() { |
| return mReadCount; |
| } |
| |
| /** |
| * @return The value of the next byte in the stream without advancing the stream. |
| * Does not affect the running count as the byte hasn't been "consumed". |
| * @throws IndexOutOfBoundsException |
| */ |
| public byte peekByte() { |
| if (available() > 0) { |
| return mBytes[mIndex + 1]; |
| } else { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * @return the next byte from the stream and advances the stream and the read count. Note |
| * that this is a signed byte (as is the case of byte in Java). The user may need to understand |
| * from context if it should be interpreted as an unsigned value. |
| * @throws IndexOutOfBoundsException |
| */ |
| public byte getByte() { |
| if (available() > 0) { |
| mReadCount++; |
| return mBytes[mIndex++]; |
| } else { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * @return the next byte from the stream and advances the stream and the read count. Note |
| * that this is an unsigned byte encoded in a Java int. |
| * @throws IndexOutOfBoundsException |
| */ |
| public int getUnsignedByte() { |
| if (available() > 0) { |
| mReadCount++; |
| return mBytes[mIndex++] & 0x000000FF; |
| } else { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * Reads 2 bytes in *little endian format* from the stream and composes a 16-bit integer. |
| * As we are storing the 2-byte value in a 4-byte integer, the upper 2 bytes are always |
| * 0, essentially making the returned value *unsigned*. |
| * @return The 16-bit integer (packed into the lower 2 bytes of an int) encoded by the |
| * next 2 bytes in the stream. |
| * @throws IndexOutOfBoundsException |
| */ |
| public int unpackUsbShort() { |
| if (available() >= 2) { |
| int b0 = getUnsignedByte(); |
| int b1 = getUnsignedByte(); |
| return (b1 << 8) | b0; |
| } else { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * Reads 3 bytes in *little endian format* from the stream and composes a 24-bit integer. |
| * As we are storing the 3-byte value in a 4-byte integer, the upper byte is always |
| * 0, essentially making the returned value *unsigned*. |
| * @return The 24-bit integer (packed into the lower 3 bytes of an int) encoded by the |
| * next 3 bytes in the stream. |
| * @throws IndexOutOfBoundsException |
| */ |
| public int unpackUsbTriple() { |
| if (available() >= 3) { |
| int b0 = getUnsignedByte(); |
| int b1 = getUnsignedByte(); |
| int b2 = getUnsignedByte(); |
| return (b2 << 16) | (b1 << 8) | b0; |
| } else { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * Reads 4 bytes in *little endian format* from the stream and composes a 32-bit integer. |
| * @return The 32-bit integer encoded by the next 4 bytes in the stream. |
| * @throws IndexOutOfBoundsException |
| */ |
| public int unpackUsbInt() { |
| if (available() >= 4) { |
| int b0 = getUnsignedByte(); |
| int b1 = getUnsignedByte(); |
| int b2 = getUnsignedByte(); |
| int b3 = getUnsignedByte(); |
| return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; |
| } else { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| /** |
| * Advances the logical position in the stream. Affects the running count also. |
| * @param numBytes The number of bytes to advance. |
| * @throws IndexOutOfBoundsException |
| * @throws IllegalArgumentException |
| */ |
| public void advance(int numBytes) { |
| if (numBytes < 0) { |
| // Positive offsets only |
| throw new IllegalArgumentException(); |
| } |
| // do arithmetic and comparison in long to avoid potential integer overflow |
| long longNewIndex = (long) mIndex + (long) numBytes; |
| if (longNewIndex <= (long) mBytes.length) { |
| mReadCount += numBytes; |
| mIndex += numBytes; |
| } else { |
| // Position the stream to the end so available() will return 0 |
| mIndex = mBytes.length; |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * Reverse the logical position in the stream. Affects the running count also. |
| * @param numBytes The (positive) number of bytes to reverse. |
| * @throws IndexOutOfBoundsException |
| * @throws IllegalArgumentException |
| */ |
| public void reverse(int numBytes) { |
| if (numBytes < 0) { |
| // Positive (reverse) offsets only |
| throw new IllegalArgumentException(); |
| } |
| if (mIndex >= numBytes) { |
| mReadCount -= numBytes; |
| mIndex -= numBytes; |
| } else { |
| mIndex = 0; |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * @return The number of bytes available to be read in the stream. |
| */ |
| public int available() { |
| return mBytes.length - mIndex; |
| } |
| } |