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.collect.ImmutableList;
20 
21 import java.nio.ByteBuffer;
22 import java.text.DecimalFormat;
23 import java.util.Collection;
24 
25 /**
26  * A UTCTime is a string representation for a UTC timestamp
27  *
28  * <p>Implements ASN.1 functionality.
29  *
30  */
31 public class Asn1UTCTime extends Asn1Object {
32   private static final Collection<Asn1Tag> possibleFirstTags =
33       ImmutableList.of(Asn1Tag.UTC_TIME);
34 
35   private int year;
36   private int month;
37   private int day;
38   private int hour;
39   private int minute;
40   private int second;
41   private DecimalFormat twoDigit = new DecimalFormat("00");
42   private DecimalFormat fourDigit = new DecimalFormat("0000");
43 
Asn1UTCTime()44   public Asn1UTCTime() {
45   }
46 
getPossibleFirstTags()47   public static Collection<Asn1Tag> getPossibleFirstTags() {
48     return possibleFirstTags;
49   }
50 
assignTo(Asn1UTCTime other)51   public void assignTo(Asn1UTCTime other) {
52     year = other.year;
53     month = other.month;
54     day = other.day;
55     hour = other.hour;
56     minute = other.minute;
57     second = other.second;
58   }
59 
getYear()60   public int getYear() {
61     return year;
62   }
63 
setYear(int newYear)64   public void setYear(int newYear) {
65     year = newYear;
66   }
67 
getMonth()68   public int getMonth() {
69     return month;
70   }
71 
setMonth(int newMonth)72   public void setMonth(int newMonth) {
73     month = newMonth;
74   }
75 
getDay()76   public int getDay() {
77     return day;
78   }
79 
setDay(int newDay)80   public void setDay(int newDay) {
81     day = newDay;
82   }
83 
getHour()84   public int getHour() {
85     return hour;
86   }
87 
setHour(int newHour)88   public void setHour(int newHour) {
89     hour = newHour;
90   }
91 
getMinute()92   public int getMinute() {
93     return minute;
94   }
95 
setMinute(int newMinute)96   public void setMinute(int newMinute) {
97     minute = newMinute;
98   }
99 
getSecond()100   public int getSecond() {
101     return second;
102   }
103 
setSecond(int newSecond)104   public void setSecond(int newSecond) {
105     second = newSecond;
106   }
107 
encodeToIA5String()108   private Asn1IA5String encodeToIA5String() {
109     StringBuilder builder = new StringBuilder();
110     builder.append(twoDigit.format(year % 100));
111     builder.append(twoDigit.format(month));
112     builder.append(twoDigit.format(day));
113     builder.append(twoDigit.format(hour % 100));
114     builder.append(twoDigit.format(minute));
115     builder.append(twoDigit.format(second));
116     builder.append("Z");
117     Asn1IA5String result = new Asn1IA5String();
118     result.setMaxSize(255);
119     result.setValue(builder.toString());
120     return result;
121   }
122 
toHumanReadableString()123   public String toHumanReadableString() {
124     StringBuilder builder = new StringBuilder();
125     builder.append(fourDigit.format(year));
126     builder.append('-');
127     builder.append(twoDigit.format(month));
128     builder.append('-');
129     builder.append(twoDigit.format(day));
130     builder.append(' ');
131     builder.append(twoDigit.format(hour));
132     builder.append(':');
133     builder.append(twoDigit.format(minute));
134     builder.append(':');
135     builder.append(twoDigit.format(second));
136     return builder.toString();
137   }
138 
getDefaultTag()139   @Override Asn1Tag getDefaultTag() {
140     return Asn1Tag.UTC_TIME;
141   }
142 
getBerValueLength()143   @Override int getBerValueLength() {
144     return encodeToIA5String().getBerValueLength();
145   }
146 
encodeBerValue(ByteBuffer buf)147   @Override void encodeBerValue(ByteBuffer buf) {
148     encodeToIA5String().encodeBerValue(buf);
149   }
150 
decodeBerValue(ByteBuffer buf)151   @Override public void decodeBerValue(ByteBuffer buf) {
152     Asn1IA5String result = new Asn1IA5String();
153     result.setMaxSize(255);
154     result.decodeBerValue(buf);
155     retrieveResult(result);
156   }
157 
158   @Override
encodePerAligned()159   public Iterable<BitStream> encodePerAligned() {
160     Asn1IA5String result = encodeToIA5String();
161     return result.encodePerAligned();
162   }
163 
164   @Override
encodePerUnaligned()165   public Iterable<BitStream> encodePerUnaligned() {
166     Asn1IA5String result = encodeToIA5String();
167     return result.encodePerUnaligned();
168   }
169 
170   // The format definition of UTCTime:
171   //
172   // http://www.obj-sys.com/asn1tutorial/node15.html
173   // http://www.obj-sys.com/asn1tutorial/node14.html
174   //
175   // We currently only support "[YY]YYMMDDHHMM[SS[Z]]"
retrieveResult(Asn1IA5String str)176   private void retrieveResult(Asn1IA5String str) {
177     String result = str.getValue();
178     int yearLength = 0;
179     // If the result has trailing 'Z', remove it.
180     if (result.charAt(result.length() - 1) == 'Z') {
181       result = result.substring(0, result.length() - 1);
182     }
183     boolean hasSecond = true;
184     switch (result.length()) {
185       case 10:
186         hasSecond = false;
187         // Fall-through
188       case 12:
189         yearLength = 2;
190         break;
191       case 14:
192         yearLength = 4;
193         break;
194       default:
195         throw new IllegalArgumentException("malformed UTCTime format: " + result);
196     }
197     year = Integer.parseInt(result.substring(0, yearLength));
198     // Two-digit year's range is from 1954 to 2053.
199     if (yearLength == 2) {
200       if (year > 53) {
201         year += 1900;
202       } else {
203         year += 2000;
204       }
205     }
206     month = Integer.parseInt(result.substring(yearLength, yearLength + 2));
207     day = Integer.parseInt(result.substring(yearLength + 2, yearLength + 4));
208     hour = Integer.parseInt(result.substring(yearLength + 4, yearLength + 6));
209     minute = Integer.parseInt(result.substring(yearLength + 6, yearLength + 8));
210     if (hasSecond) {
211       second = Integer.parseInt(result.substring(yearLength + 8, yearLength + 10));
212     } else {
213       second = 0;
214     }
215   }
216 
217   @Override
decodePerAligned(BitStreamReader reader)218   public void decodePerAligned(BitStreamReader reader) {
219     Asn1IA5String result = new Asn1IA5String();
220     result.setMaxSize(255);
221     result.decodePerAligned(reader);
222     retrieveResult(result);
223   }
224 
225   @Override
decodePerUnaligned(BitStreamReader reader)226   public void decodePerUnaligned(BitStreamReader reader) {
227     Asn1IA5String result = new Asn1IA5String();
228     result.setMaxSize(255);
229     result.decodePerUnaligned(reader);
230     retrieveResult(result);
231   }
232 }
233