1 /*
2  * Copyright (C) 2018 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 com.android.internal.telephony.uicc.euicc;
18 
19 import com.android.internal.telephony.uicc.asn1.Asn1Decoder;
20 import com.android.internal.telephony.uicc.asn1.Asn1Node;
21 import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException;
22 import com.android.internal.telephony.uicc.asn1.TagNotFoundException;
23 import com.android.telephony.Rlog;
24 
25 import java.util.Arrays;
26 
27 /**
28  * This represents the version of GSMA SGP.22 spec in the form of 3 numbers: major, minor, and
29  * revision.
30  */
31 public final class EuiccSpecVersion implements Comparable<EuiccSpecVersion> {
32     private static final String LOG_TAG = "EuiccSpecVer";
33 
34     // ASN.1 Tags
35     private static final int TAG_ISD_R_APP_TEMPLATE = 0xE0;
36     private static final int TAG_VERSION = 0x82;
37 
38     private final int[] mVersionValues = new int[3];
39 
40     /**
41      * Parses the response of opening a logical channel to get spec version of the eUICC card.
42      *
43      * @return Parsed spec version. If any error is encountered, null will be returned.
44      */
fromOpenChannelResponse(byte[] response)45     public static EuiccSpecVersion fromOpenChannelResponse(byte[] response) {
46         Asn1Node node;
47         try {
48             Asn1Decoder decoder = new Asn1Decoder(response);
49             if (!decoder.hasNextNode()) {
50                 return null;
51             }
52             node = decoder.nextNode();
53         } catch (InvalidAsn1DataException e) {
54             Rlog.e(LOG_TAG, "Cannot parse the select response of ISD-R.", e);
55             return null;
56         }
57         try {
58             byte[] versionType;
59             if (node.getTag() == TAG_ISD_R_APP_TEMPLATE) {
60                 versionType = node.getChild(TAG_VERSION).asBytes();
61             } else {
62                 versionType =
63                         node.getChild(TAG_ISD_R_APP_TEMPLATE, TAG_VERSION).asBytes();
64             }
65             if (versionType.length == 3) {
66                 return new EuiccSpecVersion(versionType);
67             } else {
68                 Rlog.e(LOG_TAG, "Cannot parse select response of ISD-R: " + node.toHex());
69             }
70         } catch (InvalidAsn1DataException | TagNotFoundException e) {
71             Rlog.e(LOG_TAG, "Cannot parse select response of ISD-R: " + node.toHex());
72         }
73         return null;
74     }
75 
EuiccSpecVersion(int major, int minor, int revision)76     public EuiccSpecVersion(int major, int minor, int revision) {
77         mVersionValues[0] = major;
78         mVersionValues[1] = minor;
79         mVersionValues[2] = revision;
80     }
81 
82     /**
83      * @param version The version bytes from ASN1 data. The length must be 3.
84      */
EuiccSpecVersion(byte[] version)85     public EuiccSpecVersion(byte[] version) {
86         mVersionValues[0] = version[0] & 0xFF;
87         mVersionValues[1] = version[1] & 0xFF;
88         mVersionValues[2] = version[2] & 0xFF;
89     }
90 
getMajor()91     public int getMajor() {
92         return mVersionValues[0];
93     }
94 
getMinor()95     public int getMinor() {
96         return mVersionValues[1];
97     }
98 
getRevision()99     public int getRevision() {
100         return mVersionValues[2];
101     }
102 
103     @Override
compareTo(EuiccSpecVersion that)104     public int compareTo(EuiccSpecVersion that) {
105         if (getMajor() > that.getMajor()) {
106             return 1;
107         } else if (getMajor() < that.getMajor()) {
108             return -1;
109         }
110         if (getMinor() > that.getMinor()) {
111             return 1;
112         } else if (getMinor() < that.getMinor()) {
113             return -1;
114         }
115         if (getRevision() > that.getRevision()) {
116             return 1;
117         } else if (getRevision() < that.getRevision()) {
118             return -1;
119         }
120         return 0;
121     }
122 
123     @Override
equals(Object obj)124     public boolean equals(Object obj) {
125         if (this == obj) {
126             return true;
127         }
128         if (obj == null || getClass() != obj.getClass()) {
129             return false;
130         }
131         return Arrays.equals(mVersionValues, ((EuiccSpecVersion) obj).mVersionValues);
132     }
133 
134     @Override
hashCode()135     public int hashCode() {
136         return Arrays.hashCode(mVersionValues);
137     }
138 
139     @Override
toString()140     public String toString() {
141         return mVersionValues[0] + "." + mVersionValues[1] + "." + mVersionValues[2];
142     }
143 }
144