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