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 */ isExtensible()57 protected abstract boolean isExtensible(); 58 59 /** 60 * Returns true if the value set is an extension value. 61 */ hasExtensionValue()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 */ getSelectionOrdinal()68 @Nullable protected abstract Integer getSelectionOrdinal(); 69 getSelectedComponent()70 @Nullable protected abstract ChoiceComponent getSelectedComponent(); 71 72 /** 73 * Returns the chosen value. 74 */ getValue()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 */ getOptionCount()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 */ createAndSetValue(boolean isExtensionValue, int ordinal)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 */ createAndSetValue(Asn1Tag tag)101 protected abstract ChoiceComponent createAndSetValue(Asn1Tag tag); 102 getDefaultTag()103 @Override Asn1Tag getDefaultTag() { 104 return DUMMY_DEFAULT_TAG; 105 } 106 isConstructed()107 @Override boolean isConstructed() { 108 return true; 109 } 110 111 112 @Override getBerValueLength()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 encodeBerValue(ByteBuffer buf)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 decodeBerValue(ByteBuffer buf)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 decodeBer(ByteBuffer buf)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 encodePerImpl(boolean aligned)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 encodePerUnaligned()245 @Override public Iterable<BitStream> encodePerUnaligned() { 246 return encodePerImpl(false); 247 } 248 encodePerAligned()249 @Override public Iterable<BitStream> encodePerAligned() { 250 return encodePerImpl(true); 251 } 252 decodePerImpl(BitStreamReader reader, boolean aligned)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 decodePerUnaligned(BitStreamReader reader)291 @Override public void decodePerUnaligned(BitStreamReader reader) { 292 decodePerImpl(reader, false); 293 } 294 decodePerAligned(BitStreamReader reader)295 @Override public void decodePerAligned(BitStreamReader reader) { 296 decodePerImpl(reader, true); 297 } 298 } 299