1 /*
2  * Copyright (C) 2021 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.compatibility.common.util;
18 
19 import android.Manifest;
20 import android.telephony.TelephonyManager;
21 
22 import androidx.annotation.Nullable;
23 import androidx.annotation.StringDef;
24 import androidx.test.InstrumentationRegistry;
25 
26 import java.util.List;
27 
28 /** Utility class for common UICC- and SIM-related operations. */
29 public final class UiccUtil {
30 
31     // A table mapping from a number to a hex character for fast encoding hex strings.
32     private static final char[] HEX_CHARS = {
33         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
34     };
35 
36     /**
37      * Data class representing a single APDU transmission.
38      *
39      * <p>Constants are defined in TS 102 221 Section 10.1.2.
40      */
41     public static final class ApduCommand {
42         public static final int INS_GET_RESPONSE = 0xC0;
43 
44         public final int cla;
45         public final int ins;
46         public final int p1;
47         public final int p2;
48         public final int p3;
49         @Nullable public final String data;
50 
ApduCommand(int cla, int ins, int p1, int p2, int p3, @Nullable String data)51         public ApduCommand(int cla, int ins, int p1, int p2, int p3, @Nullable String data) {
52             this.cla = cla;
53             this.ins = ins;
54             this.p1 = p1;
55             this.p2 = p2;
56             this.p3 = p3;
57             this.data = data;
58         }
59 
60         @Override
toString()61         public String toString() {
62             return "cla=0x"
63                     + Integer.toHexString(cla)
64                     + ", ins=0x"
65                     + Integer.toHexString(ins)
66                     + ", p1=0x"
67                     + Integer.toHexString(p1)
68                     + ", p2=0x"
69                     + Integer.toHexString(p2)
70                     + ", p3=0x"
71                     + Integer.toHexString(p3)
72                     + ", data="
73                     + data;
74         }
75     }
76 
77     /** Various APDU status words and their meanings, as defined in TS 102 221 Section 10.2.1 */
78     public static final class ApduResponse {
79         public static final String SW1_MORE_RESPONSE = "61";
80 
81         public static final String SW1_SW2_OK = "9000";
82         public static final String SW1_OK_PROACTIVE_COMMAND = "91";
83     }
84 
85     /**
86      * The hashes of all supported CTS UICC test keys and their corresponding specification.
87      *
88      * <p>For up-to-date information about the CTS SIM specification, please see
89      * https://source.android.com/devices/tech/config/uicc#validation.
90      */
91     @StringDef({UiccCertificate.CTS_UICC_LEGACY, UiccCertificate.CTS_UICC_2021})
92     public @interface UiccCertificate {
93 
94         /**
95          * Indicates compliance with the "legacy" CTS UICC specification (prior to 2021).
96          *
97          * <p>Corresponding certificate: {@code aosp-testkey}.
98          *
99          * @deprecated as of 2021, and no longer supported as of 2022.
100          */
101         @Deprecated String CTS_UICC_LEGACY = "61ED377E85D386A8DFEE6B864BD85B0BFAA5AF81";
102 
103         /**
104          * Indicates compliance with the 2021 CTS UICC specification.
105          *
106          * <p>Strongly recommended as of 2021, required as of 2022.
107          *
108          * <p>Corresponding certificate: {@code cts-uicc-2021-testkey}.
109          */
110         String CTS_UICC_2021 = "CE7B2B47AE2B7552C8F92CC29124279883041FB623A5F194A82C9BF15D492AA0";
111     }
112 
113     /**
114      * A simple check for use with {@link org.junit.Assume#assumeTrue}. Checks the carrier privilege
115      * certificates stored on the SIM and returns {@code true} if {@code requiredCert} is present.
116      *
117      * <p>Can be used either in the {@code #setUp} method if an entire class requires a particular
118      * UICC, or at the top of a specific {@code @Test} method.
119      *
120      * <p>If we had JUnit 5, we could create a much cooler {@code @RequiresUiccCertificate}
121      * annotation using {@code ExtendWith} and {@code ExecutionCondition}, but that isn't available
122      * to us yet.
123      */
uiccHasCertificate(@iccCertificate String requiredCert)124     public static boolean uiccHasCertificate(@UiccCertificate String requiredCert) {
125         TelephonyManager tm =
126                 InstrumentationRegistry.getInstrumentation()
127                         .getTargetContext()
128                         .getSystemService(TelephonyManager.class);
129         List<String> uiccCerts =
130                 ShellIdentityUtils.invokeMethodWithShellPermissions(
131                         tm,
132                         TelephonyManager::getCertsFromCarrierPrivilegeAccessRules,
133                         Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
134         return uiccCerts == null ? false : uiccCerts.contains(requiredCert);
135     }
136 
137     /**
138      * Converts a byte array into a String of hexadecimal characters.
139      *
140      * @param bytes an array of bytes
141      * @return hex string representation of bytes array
142      */
143     @Nullable
bytesToHexString(@ullable byte[] bytes)144     public static String bytesToHexString(@Nullable byte[] bytes) {
145         if (bytes == null) return null;
146 
147         StringBuilder ret = new StringBuilder(2 * bytes.length);
148 
149         for (int i = 0; i < bytes.length; i++) {
150             int b;
151             b = 0x0f & (bytes[i] >> 4);
152             ret.append(HEX_CHARS[b]);
153             b = 0x0f & bytes[i];
154             ret.append(HEX_CHARS[b]);
155         }
156 
157         return ret.toString();
158     }
159 }
160