• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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