1 /*
2 * Copyright (C) 2024 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 // C++ reimplementation of f/b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
18
19 #include <libminradio/sim/IccUtils.h>
20
21 #include <android-base/logging.h>
22 #include <libminradio/sim/IccConstants.h>
23
24 namespace android::hardware::radio::minimal::sim {
25
26 using namespace ::android::hardware::radio::minimal::sim::constants;
27 namespace aidl = ::aidl::android::hardware::radio::sim;
28
29 // frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/AdnRecord.java
30 // 3GPP TS 31.102 4.2.26
31 constexpr int ADN_FOOTER_SIZE_BYTES = 14;
32 constexpr uint8_t ADN_UNUSED = 0xFF;
33 constexpr int ADN_BCD_NUMBER_LENGTH = 0;
34 constexpr int ADN_TON_AND_NPI = 1;
35 constexpr int ADN_DIALING_NUMBER_START = 2;
36 constexpr int ADN_DIALING_NUMBER_END = 11;
37
38 // com.android.internal.telephony.uicc.IccUtils.charToByte
39 // com.android.internal.telephony.uicc.IccUtils.hexCharToInt
charToByte(char c)40 static uint8_t charToByte(char c) {
41 if (c >= '0' && c <= '9') {
42 return c - '0';
43 }
44 if (c >= 'A' && c <= 'F') {
45 return c - 'A' + 10;
46 }
47 if (c >= 'a' && c <= 'f') {
48 return c - 'a' + 10;
49 }
50 LOG(FATAL) << "IccUtils.charToByte: invalid hex character: " << static_cast<int>(c);
51 return 0;
52 }
53
54 static constexpr char kHexChars[] = "0123456789ABCDEF";
55
toIccIoResult(uint16_t errorCode,std::string_view simResponse)56 static aidl::IccIoResult toIccIoResult(uint16_t errorCode, std::string_view simResponse) {
57 return {
58 .sw1 = errorCode >> 8,
59 .sw2 = errorCode & 0xFF,
60 .simResponse = std::string(simResponse),
61 };
62 }
63
toIccIoResult(std::span<uint8_t const> bytes)64 aidl::IccIoResult toIccIoResult(std::span<uint8_t const> bytes) {
65 return toIccIoResult(IO_RESULT_SUCCESS, sim::bytesToHexString(bytes));
66 }
67
toIccIoResult(std::vector<uint8_t> && bytes)68 aidl::IccIoResult toIccIoResult(std::vector<uint8_t>&& bytes) {
69 return toIccIoResult(IO_RESULT_SUCCESS, sim::bytesToHexString(bytes));
70 }
71
toIccIoResult(std::string_view simResponse)72 aidl::IccIoResult toIccIoResult(std::string_view simResponse) {
73 return toIccIoResult(IO_RESULT_SUCCESS, simResponse);
74 }
75
toIccIoResult(uint16_t errorCode)76 aidl::IccIoResult toIccIoResult(uint16_t errorCode) {
77 return toIccIoResult(errorCode, "");
78 }
79
80 // com.android.internal.telephony.uicc.IccUtils.hexStringToBytes
hexStringToBytes(std::string_view str)81 std::vector<uint8_t> hexStringToBytes(std::string_view str) {
82 CHECK(str.size() % 2 == 0) << "Hex string length not even";
83 std::vector<uint8_t> bytes(str.size() / 2);
84 for (size_t i = 0; i < bytes.size(); i++) {
85 bytes[i] = charToByte(str[i * 2]) << 4 | charToByte(str[i * 2 + 1]);
86 }
87 return bytes;
88 }
89
90 // com.android.internal.telephony.uicc.IccUtils.bchToString (inversion)
91 // NOTE: BCH is a nibble-swizzled bytes reprezentation
hexStringToBch(std::string_view str)92 std::vector<uint8_t> hexStringToBch(std::string_view str) {
93 CHECK(str.size() % 2 == 0) << "Hex string length not even";
94 std::vector<uint8_t> bch(str.size() / 2);
95 for (size_t i = 0; i < bch.size(); i++) {
96 bch[i] = charToByte(str[i * 2]) | charToByte(str[i * 2 + 1]) << 4;
97 }
98 return bch;
99 }
100
101 // com.android.internal.telephony.uicc.IccUtils.bytesToHexString
bytesToHexString(std::span<uint8_t const> bytes)102 std::string bytesToHexString(std::span<uint8_t const> bytes) {
103 std::string ret(bytes.size() * 2, '0');
104 for (size_t i = 0; i < bytes.size(); i++) {
105 ret[i * 2 + 0] = kHexChars[0x0F & (bytes[i] >> 4)];
106 ret[i * 2 + 1] = kHexChars[0x0F & (bytes[i])];
107 }
108 return ret;
109 }
110
bytesToHexString(std::vector<uint8_t> && bytes)111 std::string bytesToHexString(std::vector<uint8_t>&& bytes) {
112 std::span<uint8_t> bytesSpan(bytes);
113 return bytesToHexString(bytesSpan);
114 }
115
116 // com.android.internal.telephony.uicc.IccUtils.bchToString
bchToHexString(std::span<uint8_t const> bytes)117 std::string bchToHexString(std::span<uint8_t const> bytes) {
118 std::string ret(bytes.size() * 2, '0');
119 for (size_t i = 0; i < bytes.size(); i++) {
120 ret[i * 2 + 0] = kHexChars[0x0F & (bytes[i])];
121 ret[i * 2 + 1] = kHexChars[0x0F & (bytes[i] >> 4)];
122 }
123 return ret;
124 }
125
uint8ToBytes(uint8_t val)126 std::vector<uint8_t> uint8ToBytes(uint8_t val) {
127 return {val};
128 }
129
uint16ToBytes(uint16_t val)130 std::vector<uint8_t> uint16ToBytes(uint16_t val) {
131 return {
132 static_cast<uint8_t>(val >> 8),
133 static_cast<uint8_t>(val & 0xFF),
134 };
135 }
136
137 // com.android.internal.telephony.uicc.IccUtils.bcdToString (inversion)
138 // integerString is a number with possible leading zeros
stringToBcd(std::string_view intString)139 static std::vector<uint8_t> stringToBcd(std::string_view intString) {
140 // Note: 3GPP TS 31.102 Table 4.4 describes BCD coding for characters * and # (not implemented)
141 bool isOdd = intString.size() % 2 == 1;
142 std::vector<uint8_t> ret(intString.size() / 2 + (isOdd ? 1 : 0), 0);
143 for (size_t i = 0; i < intString.size(); i++) {
144 const char digitC = intString[i];
145 CHECK(digitC >= '0' && digitC <= '9') << "Invalid numeric string: " << intString;
146 uint8_t digit = digitC - '0';
147
148 if (i % 2 == 1) digit <<= 4;
149 ret[i / 2] |= digit;
150 }
151 if (isOdd) {
152 *ret.rbegin() |= 0xF0;
153 }
154 return ret;
155 }
156
157 // com.android.internal.telephony.uicc.IccUtils.stringToBcdPlmn
stringToBcdPlmn(std::string_view plmn,std::vector<uint8_t> & data,size_t offset)158 static void stringToBcdPlmn(std::string_view plmn, std::vector<uint8_t>& data, size_t offset) {
159 char digit6 = plmn.length() > 5 ? plmn[5] : 'F';
160 data[offset] = (charToByte(plmn[1]) << 4) | charToByte(plmn[0]);
161 data[offset + 1] = (charToByte(digit6) << 4) | charToByte(plmn[2]);
162 data[offset + 2] = (charToByte(plmn[4]) << 4) | charToByte(plmn[3]);
163 }
164
165 // com.android.internal.telephony.uicc.IccUtils.encodeFplmns
encodeFplmns(std::span<std::string_view> fplmns)166 std::vector<uint8_t> encodeFplmns(std::span<std::string_view> fplmns) {
167 // 3GPP TS 31.102 4.2.16
168 auto recordsCount = std::max<size_t>(fplmns.size(), 4);
169 std::vector<uint8_t> serializedFplmns(recordsCount * FPLMN_BYTE_SIZE, 0xFF);
170
171 size_t record = 0;
172 for (auto&& fplmn : fplmns) {
173 stringToBcdPlmn(fplmn, serializedFplmns, FPLMN_BYTE_SIZE * record++);
174 }
175 return serializedFplmns;
176 }
177
encodeMsisdn(std::string_view phoneNumber)178 std::vector<uint8_t> encodeMsisdn(std::string_view phoneNumber) {
179 // 3GPP TS 31.102 4.2.26
180 std::vector<uint8_t> msisdn(ADN_FOOTER_SIZE_BYTES, ADN_UNUSED);
181 bool isInternational = phoneNumber.size() >= 1 && phoneNumber[0] == '+';
182 if (isInternational) phoneNumber = phoneNumber.substr(1);
183
184 auto encodedNumber = stringToBcd(phoneNumber);
185 constexpr int numberMaxSize = ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1;
186 if (encodedNumber.size() > numberMaxSize) {
187 encodedNumber.resize(numberMaxSize);
188 }
189
190 msisdn[ADN_BCD_NUMBER_LENGTH] = 1 + encodedNumber.size();
191
192 // 3GPP TS 24.008 Table 10.5.91:
193 // 0b1xxxxxx - mandatory bit
194 // ton (type of number):
195 // - 0bx001xxxx - international number (with +)
196 // - 0bx010xxxx - national number
197 // npi (numbering plan identification):
198 // - 0bxxxx0001 - ISDN/telephony numbering plan
199 msisdn[ADN_TON_AND_NPI] = isInternational ? 0b10010001 : 0b10100001;
200
201 std::copy(encodedNumber.begin(), encodedNumber.end(),
202 std::next(msisdn.begin(), ADN_DIALING_NUMBER_START));
203
204 return msisdn;
205 }
206
encodeAd(uint8_t mncLength)207 std::vector<uint8_t> encodeAd(uint8_t mncLength) {
208 // ETSI TS 131 102 4.2.18
209 CHECK(mncLength == 2 || mncLength == 3) << "Invalid MNC length: " << mncLength;
210
211 std::vector<uint8_t> ad(4);
212 ad[3] = mncLength;
213 return ad;
214 }
215
216 } // namespace android::hardware::radio::minimal::sim
217