Home | History | Annotate | Download | only in descriptors
      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      */
     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      */
     71     public void resetReadCount() {
     72         mReadCount = 0;
     73     }
     74 
     75     /**
     76      * Retrieves the running count of bytes read from the stream.
     77      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    220     public int available() {
    221         return mBytes.length - mIndex;
    222     }
    223 }
    224