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 com.google.common.base.Preconditions;
     20 import com.google.common.collect.ImmutableList;
     21 
     22 import java.nio.ByteBuffer;
     23 
     24 import javax.annotation.Nullable;
     25 
     26 /**
     27  * Implements ASN.1 functionality.
     28  *
     29  */
     30 public abstract class Asn1Choice extends Asn1Object {
     31   /**
     32    * Dummy non-tag used as default tag for CHOICE, which reflects the BER encoding idea
     33    * that CHOICE is not an object in itself, it just gets coded as its value. This means
     34    * that it doesn't matter whether the choice object tag is designated as IMPLICIT or
     35    * EXPLICIT, it gets coded the same way anyway.
     36    */
     37   private static final Asn1Tag DUMMY_DEFAULT_TAG = new Asn1Tag(null, Integer.MIN_VALUE) {
     38     @Override
     39     public int getTaggedLength(int valueLength) {
     40       return valueLength;
     41     }
     42 
     43     @Override
     44     public void writeTagAndLength(ByteBuffer buf, boolean constructed, int valueLength) {
     45       // Do nothing.
     46     }
     47 
     48     @Override
     49     int getValue() {
     50       throw new UnsupportedOperationException("This is not a real tag.");
     51     }
     52   };
     53 
     54   /**
     55    * Returns true if the type has an extension marker.
     56    */
     57   protected abstract boolean isExtensible();
     58 
     59   /**
     60    * Returns true if the value set is an extension value.
     61    */
     62   protected abstract boolean hasExtensionValue();
     63 
     64   /**
     65    * Returns the ordinal of the chosen value (extension values re-start at 0),
     66    * or null if no value is set.
     67    */
     68   @Nullable protected abstract Integer getSelectionOrdinal();
     69 
     70   @Nullable protected abstract ChoiceComponent getSelectedComponent();
     71 
     72   /**
     73    * Returns the chosen value.
     74    */
     75   protected abstract Asn1Object getValue();
     76 
     77   /**
     78    * Returns the number of choices available for the current value of
     79    * {@code hasExtensionValue()}. If no value has been set, the number of
     80    * choices for a non-extension value is returned.
     81    */
     82   protected abstract int getOptionCount();
     83 
     84   /**
     85    * Creates a new instance of the type specified by the parameters, sets
     86    * the current value of this instance to it and returns the newly created
     87    * value.
     88    */
     89   protected abstract Asn1Object createAndSetValue(boolean isExtensionValue,
     90                                                   int ordinal);
     91 
     92   /**
     93    * Given an Asn1Tag, creates and sets the value of this enumeration to a newly created object.
     94    * Used in BER decoding.
     95    *
     96    * @param tag the tag read from the input stream (less the "constructed" bit)
     97    * @return the newly created enumeration component
     98    * @throws IllegalArgumentException if the tag is not recognized. This currently includes
     99    * extension values.
    100    */
    101   protected abstract ChoiceComponent createAndSetValue(Asn1Tag tag);
    102 
    103   @Override Asn1Tag getDefaultTag() {
    104     return DUMMY_DEFAULT_TAG;
    105   }
    106 
    107   @Override boolean isConstructed() {
    108     return true;
    109   }
    110 
    111 
    112   @Override
    113   public int getBerValueLength() {
    114     if (hasExtensionValue()) {
    115       throw new UnsupportedOperationException("BER for extension values is unsupported");
    116     }
    117     ChoiceComponent select = getSelectedComponent();
    118     if (select == null) {
    119       throw new NullPointerException("No component set");
    120     }
    121     if (select.getTag() != null) {
    122       int valueLen;
    123       if (select.isImplicitTagging()) {
    124         valueLen = getValue().getBerValueLength();
    125       } else {
    126         valueLen = getValue().getBerLength();
    127       }
    128       return select.getTag().getTaggedLength(valueLen);
    129     }
    130     return getValue().getBerLength();
    131   }
    132 
    133   @Override
    134   public void encodeBerValue(ByteBuffer buf) {
    135     if (hasExtensionValue()) {
    136       throw new UnsupportedOperationException("BER for extension values is unsupported");
    137     }
    138     ChoiceComponent select = getSelectedComponent();
    139     if (select == null) {
    140       throw new NullPointerException("No component set");
    141     }
    142     Asn1Object value = getValue();
    143     if (select.getTag() != null) {
    144       if (select.isImplicitTagging()) {
    145         select.getTag().writeTagAndLength(buf, value.isConstructed(), value.getBerValueLength());
    146         value.encodeBerValue(buf);
    147       } else {
    148         select.getTag().writeTagAndLength(buf, true, value.getBerLength());
    149         value.encodeBer(buf);
    150       }
    151     } else {
    152       value.encodeBer(buf);
    153     }
    154   }
    155 
    156   @Override
    157   public void decodeBerValue(ByteBuffer buf) {
    158     Asn1Tag tag = Asn1Tag.peekTag(buf);
    159     ChoiceComponent select = createAndSetValue(tag);
    160     if (select == null) {
    161       throw new IllegalArgumentException("Unknown selection tag: " + tag);
    162     }
    163     Asn1Object element = getValue();
    164     if (select.getTag() != null) {
    165       Asn1Tag.readTag(buf); // Read the tag off.
    166       int valueLen = Asn1Tag.readLength(buf);
    167 
    168       if (valueLen != buf.remaining()) {
    169         throw new IllegalArgumentException("Length mismatch: expected=" + buf.remaining()
    170             + ", actual=" + valueLen);
    171       }
    172 
    173       if (select.isImplicitTagging()) {
    174         element.decodeBerValue(buf);
    175         return;
    176       }
    177     }
    178     element.decodeBer(buf);
    179   }
    180 
    181   @Override
    182   public void decodeBer(ByteBuffer buf) {
    183     if (getTag() != null) {
    184       // This CHOICE is tagged
    185       Asn1Tag tag = Asn1Tag.readTag(buf);
    186       int len = Asn1Tag.readLength(buf);
    187       checkTag(tag, getTag());
    188     }
    189     decodeBerValue(buf);
    190   }
    191 
    192   private Iterable<BitStream> encodePerImpl(boolean aligned) {
    193     ImmutableList.Builder<BitStream> listBuilder = ImmutableList.builder();
    194     if (isExtensible()) {
    195       BitStream extensionMarker = new BitStream();
    196       extensionMarker.appendBit(hasExtensionValue());
    197       listBuilder.add(extensionMarker);
    198     }
    199 
    200     int optionCount = getOptionCount();
    201     Integer selectionOrdinal = getSelectionOrdinal();
    202     Preconditions.checkState(optionCount == 0 || selectionOrdinal != null,
    203                              "No value set.");
    204     if (hasExtensionValue()) {
    205       if (aligned) {
    206         listBuilder.addAll(
    207             PerAlignedUtils.encodeNormallySmallWholeNumber(selectionOrdinal));
    208       } else {
    209         listBuilder.addAll(
    210             PerUnalignedUtils.encodeNormallySmallWholeNumber(
    211               selectionOrdinal));
    212       }
    213     } else if (optionCount > 1) {
    214       if (aligned) {
    215         listBuilder.add(
    216             PerAlignedUtils.encodeSmallConstrainedWholeNumber(
    217                 selectionOrdinal, 0, optionCount - 1));
    218       } else {
    219         listBuilder.add(
    220             PerUnalignedUtils.encodeConstrainedWholeNumber(
    221                 selectionOrdinal, 0, optionCount - 1));
    222       }
    223     }
    224 
    225     if (optionCount > 0) {
    226       Asn1Object value = getValue();
    227       if (hasExtensionValue()) {
    228         if (aligned) {
    229           listBuilder.addAll(PerAlignedUtils.encodeOpenTypeField(value));
    230         } else {
    231           listBuilder.addAll(
    232               PerUnalignedUtils.encodeOpenTypeField(value));
    233         }
    234       } else {
    235         if (aligned) {
    236           listBuilder.addAll(value.encodePerAligned());
    237         } else {
    238           listBuilder.addAll(value.encodePerUnaligned());
    239         }
    240       }
    241     }
    242     return listBuilder.build();
    243   }
    244 
    245   @Override public Iterable<BitStream> encodePerUnaligned() {
    246     return encodePerImpl(false);
    247   }
    248 
    249   @Override public Iterable<BitStream> encodePerAligned() {
    250     return encodePerImpl(true);
    251   }
    252 
    253   private void decodePerImpl(BitStreamReader reader, boolean aligned) {
    254     boolean extensionValued = false;
    255     Integer selectionOrdinal = 0;
    256     if (isExtensible()) {
    257       extensionValued = reader.readBit();
    258     }
    259     if (extensionValued) {
    260       if (aligned) {
    261         selectionOrdinal = PerAlignedUtils.decodeNormallySmallWholeNumber(reader);
    262       } else {
    263         selectionOrdinal = PerUnalignedUtils.decodeNormallySmallWholeNumber(reader);
    264       }
    265     } else if (getOptionCount() > 1) {
    266       if (aligned) {
    267         selectionOrdinal = PerAlignedUtils.decodeSmallConstrainedWholeNumber(
    268             reader, 0, getOptionCount() - 1);
    269       } else {
    270         selectionOrdinal = PerUnalignedUtils.decodeConstrainedWholeNumber(
    271             reader, 0, getOptionCount() - 1);
    272       }
    273     }
    274     if (extensionValued) {
    275       Asn1Object element = createAndSetValue(extensionValued, selectionOrdinal);
    276       if (aligned) {
    277         PerAlignedUtils.decodeOpenTypeField(reader, element);
    278       } else {
    279         PerUnalignedUtils.decodeOpenTypeField(reader, element);
    280       }
    281     } else if (getOptionCount() > 0) {
    282       Asn1Object element = createAndSetValue(extensionValued, selectionOrdinal);
    283       if (aligned) {
    284         element.decodePerAligned(reader);
    285       } else {
    286         element.decodePerUnaligned(reader);
    287       }
    288     }
    289   }
    290 
    291   @Override public void decodePerUnaligned(BitStreamReader reader) {
    292     decodePerImpl(reader, false);
    293   }
    294 
    295   @Override public void decodePerAligned(BitStreamReader reader) {
    296     decodePerImpl(reader, true);
    297   }
    298 }
    299