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