Home | History | Annotate | Download | only in base
      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 
     17 package android.location.cts.asn1.base;
     18 
     19 import java.nio.BufferOverflowException;
     20 import java.nio.ByteBuffer;
     21 import java.nio.ReadOnlyBufferException;
     22 import java.util.Collection;
     23 
     24 import javax.annotation.Nullable;
     25 
     26 /**
     27  * Specifies ASN.1 functionality needed for all ASN.1 objects.
     28  *
     29  */
     30 public abstract class Asn1Object {
     31   /**
     32    * Returns the tags that can decode to this type. Subclasses are expected to "fauxverride".
     33    */
     34   public static Collection<Asn1Tag> getPossibleFirstTags() {
     35     throw new UnsupportedOperationException("No first tags defined");
     36   }
     37 
     38   /**
     39    * Returns the universal tag defined for the base type.
     40    */
     41   abstract Asn1Tag getDefaultTag();
     42 
     43   /**
     44    * Returns the declared tag for this type. Tagged subclasses must override, the
     45    * default implementation returns null.
     46    */
     47   @Nullable protected Asn1Tag getTag() {
     48     return null;
     49   }
     50 
     51   /**
     52    * Returns true if implicit tagging is to be applied to the type. Tagged subclasses must
     53    * override, the default implementation throws.
     54    */
     55   protected boolean isTagImplicit() {
     56     throw new IllegalStateException("Not a tagged subclass");
     57   }
     58 
     59   /**
     60    * Returns true if this object is "constructed", that is, it's composed of further TLVs
     61    * (as opposed to being primitive).
     62    */
     63   boolean isConstructed() {
     64     return false;
     65   }
     66 
     67   /**
     68    * Returns the number of octets in the BER encoding of this object. Called by
     69    * {@link #encodeBer()} to allocate a buffer of the correct size before starting
     70    * the encoding.
     71    */
     72   int getBerLength() {
     73     int valueLen = getBerValueLength();
     74     Asn1Tag tag = getTag();
     75     if (tag == null) {
     76       tag = getDefaultTag();
     77     } else if (!isTagImplicit()) {
     78       valueLen = getDefaultTag().getTaggedLength(valueLen);
     79     }
     80     return tag.getTaggedLength(valueLen);
     81   }
     82 
     83   /**
     84    * Returns the number of octets required to encoded the value of this {@link Asn1Object}.
     85    */
     86   abstract int getBerValueLength();
     87 
     88   /**
     89    * Returns the BER encoding of this object.
     90    */
     91   public byte[] encodeBer() {
     92     ByteBuffer buf = ByteBuffer.allocate(getBerLength());
     93     encodeBer(buf);
     94     return buf.array();
     95   }
     96 
     97   void encodeBer(ByteBuffer buf) {
     98     int valueLen = getBerValueLength();
     99     Asn1Tag tag = getTag();
    100     if (tag == null) {
    101       tag = getDefaultTag();
    102     } else {
    103       if (!isTagImplicit()) {
    104         int innerValueLen = getDefaultTag().getTaggedLength(valueLen);
    105         tag.writeTagAndLength(buf, true, innerValueLen);
    106         tag = getDefaultTag();
    107       }
    108     }
    109     tag.writeTagAndLength(buf, isConstructed(), valueLen);
    110     encodeBerValue(buf);
    111   }
    112 
    113   /**
    114    * Writes the BER encoded value of this {@link Asn1Object} into the specified buffer.
    115    *
    116    * @param buf the byte buffer to write to
    117    * @throws BufferOverflowException If there is insufficient space in the buffer
    118    * @throws ReadOnlyBufferException If the buffer is read-only
    119    */
    120   abstract void encodeBerValue(ByteBuffer buf);
    121 
    122   /**
    123    * BER decodes the provided buffer. Should only be called on newly created instances as subclasses
    124    * may not write optional fields not explicitly present in the input.
    125    *
    126    * @param data the BER encoded input
    127    * @throws IllegalArgumentException in case of parsing failure
    128    */
    129   public void decodeBer(byte[] data) {
    130     decodeBer(ByteBuffer.wrap(data));
    131   }
    132 
    133   void decodeBer(ByteBuffer buf) {
    134     Asn1Tag tag = Asn1Tag.readTag(buf);
    135     int valueLen = Asn1Tag.readLength(buf);
    136 
    137     if (valueLen != buf.remaining()) {
    138       throw new IllegalArgumentException("Length mismatch: expected=" + buf.remaining()
    139           + ", actual=" + valueLen);
    140     }
    141 
    142     if (getTag() != null) {
    143       checkTag(tag, getTag());
    144       if (!isTagImplicit()) {
    145         // read inner tag and length
    146         Asn1Tag innerTag = Asn1Tag.readTag(buf);
    147         int innerValueLen = Asn1Tag.readLength(buf);
    148         if (innerValueLen != buf.remaining()) {
    149           throw new IllegalArgumentException("Length mismatch: expected=" + buf.remaining()
    150               + ", actual=" + innerValueLen);
    151         }
    152         checkTag(innerTag, getDefaultTag());
    153       }
    154     }
    155 
    156     decodeBerValue(buf);
    157 
    158     if (buf.hasRemaining()) {
    159       throw new IllegalArgumentException("BER encoded input not fully read");
    160     }
    161   }
    162 
    163   void checkTag(Asn1Tag actual, Asn1Tag expected) {
    164     if (!expected.equals(actual)) {
    165       throw new IllegalArgumentException("Invalid tag: expected=" + expected
    166           + ", actual=" + actual);
    167     }
    168   }
    169 
    170   /**
    171    * Decodes the BER encoded value of this {@link Asn1Object}. On return from this method the
    172    * provided buffer should be empty.
    173    */
    174   abstract void decodeBerValue(ByteBuffer buf);
    175 
    176   /**
    177    * Returns remaining bytes in the {@link ByteBuffer} in a newly allocated byte array of exactly
    178    * the right size. The ByteBuffer will be empty upon return.
    179    */
    180   static byte[] getRemaining(ByteBuffer buf) {
    181     byte[] bytes = new byte[buf.remaining()];
    182     buf.get(bytes);
    183     return bytes;
    184   }
    185 
    186   public abstract Iterable<BitStream> encodePerAligned();
    187 
    188   public abstract Iterable<BitStream> encodePerUnaligned();
    189 
    190   /**
    191    * This method should only be called on a newly created instance to avoid
    192    * having residue state in it.
    193    */
    194   public abstract void decodePerUnaligned(BitStreamReader reader);
    195 
    196 
    197   /**
    198    * This method should only be called on a newly created instance to avoid
    199    * having residue state in it.
    200    */
    201   public abstract void decodePerAligned(BitStreamReader reader);
    202 
    203   public String toIndentedString(String indent) {
    204     return indent + toString();
    205   }
    206 }
    207