1 //
2 // Copyright (C) 2020 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 #include "host/commands/modem_simulator/pdu_parser.h"
17
18 #include <algorithm>
19 #include <chrono>
20 #include <ctime>
21 #include <iomanip>
22 #include <sstream>
23 #include <string>
24 #include <thread>
25 namespace cuttlefish {
26
27 static const std::string kWithoutServiceCenterAddress = "00";
28 static const std::string kStatusReportIndicator = "06";
29 static const std::string kSRIAndMMSIndicator = "24"; /* SRI is 1 && MMS is 1*/
30 static const std::string kUDHIAndSRIAndMMSIndicator = "64"; /* UDHI is 1 && SRI is 1 && MMS is 1*/
31
PDUParser(std::string & pdu)32 PDUParser::PDUParser(std::string &pdu) {
33 is_valid_pdu_ = DecodePDU(pdu);
34 }
35
IsValidPDU()36 bool PDUParser::IsValidPDU() {
37 return is_valid_pdu_;
38 }
39
40 /**
41 * PDU format:
42 * SCA PDU-Type MR OA PID DCS VP UDL UD
43 * bytes: 1-12 1 1 2-12 1 1 0 1 0-140
44 * eg. 00 21 00 0B 91 5155255155F4 00 00 0C AB58AD56ABC962B55A8D06
45 */
46 // 00 01 00 05 81 0180F6 00 00 0D 61B2996C0691CD6433190402
DecodePDU(std::string & pdu)47 bool PDUParser::DecodePDU(std::string& pdu) {
48 // At least: SCA(1) + PDU-Type(1) + MR(1) + OA(2) + PID(1) + DSC(1) + UDL(1)
49 auto pdu_total_length = pdu.size();
50 if (pdu_total_length < 8) {
51 return false;
52 }
53
54 std::string_view pdu_view = pdu;
55 size_t pos = 0;
56
57 /* 1. SMSC Address Length: 1 byte */
58 std::string temp = pdu.substr(0, 2);
59 pos += 2;
60 if (temp != kWithoutServiceCenterAddress) {
61 auto smsc_length = Hex2ToByte(temp);
62 pos += smsc_length * 2; // Skip SMSC Address
63 }
64
65 /* 2. PDU-Type: 1 byte */
66 pdu_type_ = pdu_view.substr(std::min(pos, pdu_total_length), 2);
67 pos += 2;
68
69 /* 3. MR: 1 byte */
70 message_reference_ = pdu_view.substr(std::min(pos, pdu_total_length), 2);
71 pos += 2;
72
73 /* 4. Originator Address Length: 1 byte */
74 temp = pdu_view.substr(std::min(pos, pdu_total_length), 2);
75 auto oa_length = Hex2ToByte(temp);
76 if (oa_length & 0x01) {
77 oa_length += 1;
78 }
79
80 /* 5. Originator Address including OA length */
81 originator_address_ = pdu_view.substr(std::min(pos, pdu_total_length), (oa_length + 4));
82 pos += (oa_length + 4);
83
84 /* 6. Protocol ID: 1 byte */
85 protocol_id_ = pdu_view.substr(std::min(pos, pdu_total_length), 2);
86 pos += 2;
87
88 /* 7. Data Code Scheme: 1 byte */
89 data_code_scheme_ = pdu_view.substr(std::min(pos, pdu_total_length), 2);
90 pos += 2;
91
92 /* 8. User Data Length: 1 byte */
93 temp = pdu_view.substr(std::min(pos, pdu_total_length), 2);
94 auto ud_length = Hex2ToByte(temp);
95
96 /* 9. User Data including UDL */
97 user_data_ = pdu_view.substr(std::min(pos, pdu_total_length));
98
99 if (data_code_scheme_ == "00") { // GSM_7BIT
100 pos += ud_length * 2 + 2;
101 int offset = ud_length / 8;
102 pos -= offset * 2;
103 } else if (data_code_scheme_ == "08") { // GSM_UCS2
104 pos += ud_length * 2 + 2;
105 } else {
106 pos += ud_length * 2 + 2;
107 }
108 if (pos == pdu_total_length) {
109 return true;
110 }
111
112 return false;
113 }
114
115 /**
116 * The PDU-Type of receiver
117 * BIT 7 6 5 4 3 2 1 0
118 * Param RP UDHI SRI - - MMS MTI MTI
119 * When SRR bit is 1, it represents that SMS status report should be reported.
120 */
CreatePDU()121 std::string PDUParser::CreatePDU() {
122 if (!is_valid_pdu_) {
123 return "";
124 }
125
126 // Ignore SMSC address, default to be '00'
127 std::string pdu = kWithoutServiceCenterAddress;
128 int pdu_type = Hex2ToByte(pdu_type_);
129
130 if (pdu_type & 0x40) {
131 pdu += kUDHIAndSRIAndMMSIndicator;
132 } else {
133 pdu += kSRIAndMMSIndicator;
134 }
135
136 pdu += originator_address_ + protocol_id_ + data_code_scheme_;
137 pdu += GetCurrentTimeStamp();
138 pdu += user_data_;
139
140 return pdu;
141 }
142
143 /**
144 * the PDU-Type of sender
145 * BIT 7 6 5 4 3 2 1 0
146 * Param RP UDHI SRR VPF VPF RD MTI MTI
147 * When SRR bit is 1, it represents that SMS status report should be reported.
148 */
IsNeededStatuReport()149 bool PDUParser::IsNeededStatuReport() {
150 if (!is_valid_pdu_) {
151 return false;
152 }
153
154 int pdu_type = Hex2ToByte(pdu_type_);
155 if (pdu_type & 0x20) {
156 return true;
157 }
158
159 return false;
160 }
161
CreateStatuReport(int message_reference)162 std::string PDUParser::CreateStatuReport(int message_reference) {
163 if (!is_valid_pdu_) {
164 return "";
165 }
166
167 std::string pdu = kWithoutServiceCenterAddress;
168 pdu += kStatusReportIndicator;
169
170 std::stringstream ss;
171 ss << std::setfill('0') << std::setw(2) << std::hex << message_reference;
172 pdu += ss.str();
173
174 pdu += originator_address_;
175 pdu += GetCurrentTimeStamp();
176 std::this_thread::sleep_for(std::chrono::seconds(1));
177 pdu += GetCurrentTimeStamp();
178 pdu += "00"; /* "00" means that SMS have been sent successfully */
179
180 return pdu;
181 }
182
CreateRemotePDU(std::string & host_port)183 std::string PDUParser::CreateRemotePDU(std::string& host_port) {
184 if (host_port.size() != 4 || !is_valid_pdu_) {
185 return "";
186 }
187
188 std::string pdu = kWithoutServiceCenterAddress + pdu_type_ + message_reference_;
189
190 // Remove the remote port
191 std::string number = GetPhoneNumberFromAddress();
192 auto new_phone_number = number.substr(0, number.size() - 4);;
193 new_phone_number.append(host_port);
194 if (new_phone_number.size() & 1) {
195 new_phone_number.append("F");
196 }
197
198 // Add OA length and type
199 pdu += originator_address_.substr(0,
200 originator_address_.size() - new_phone_number .size());
201 pdu += BCDToString(new_phone_number); // Add local host port
202 pdu += protocol_id_;
203 pdu += data_code_scheme_;
204 pdu += user_data_;
205
206 return pdu;
207 }
208
GetPhoneNumberFromAddress()209 std::string PDUParser::GetPhoneNumberFromAddress() {
210 if (!is_valid_pdu_) {
211 return "";
212 }
213
214 // Skip OA length and type
215 std::string address;
216 if (originator_address_.size() == 18) {
217 address = originator_address_.substr(6);
218 } else {
219 address = originator_address_.substr(4);
220 }
221
222 return BCDToString(address);
223 }
224
HexCharToInt(char c)225 int PDUParser::HexCharToInt(char c) {
226 if (c >= '0' && c <= '9') {
227 return (c - '0');
228 }
229 if (c >= 'A' && c <= 'F') {
230 return (c - 'A' + 10);
231 }
232 if (c >= 'a' && c <= 'f') {
233 return (c - 'a' + 10);
234 }
235
236 return -1; // Invalid hex char
237 }
238
Hex2ToByte(const std::string & hex)239 int PDUParser::Hex2ToByte(const std::string& hex) {
240 int hi = HexCharToInt(hex[0]);
241 int lo = HexCharToInt(hex[1]);
242
243 if (hi < 0 || lo < 0) {
244 return -1;
245 }
246
247 return ( (hi << 4) | lo );
248 }
249
IntToHexString(int value)250 std::string PDUParser::IntToHexString(int value) {
251 int hi = value / 10;
252 int lo = value % 10;
253 return std::to_string(lo) + std::to_string(hi);
254 }
255
IntToHexStringTimeZoneDiff(int tzdiff_hour)256 std::string PDUParser::IntToHexStringTimeZoneDiff(int tzdiff_hour) {
257 // https://en.wikipedia.org/wiki/GSM_03.40
258 int delta = 0;
259 if (tzdiff_hour < 0) {
260 tzdiff_hour = -tzdiff_hour;
261 delta = 8;
262 }
263 const int tzdiff_quarter_hour = 4 * tzdiff_hour;
264 const int hi = tzdiff_quarter_hour / 10 + delta;
265 const int lo = tzdiff_quarter_hour % 10;
266 std::stringstream ss;
267 ss << std::hex << lo;
268 ss << std::hex << hi;
269 return ss.str();
270 }
271
BCDToString(std::string & data)272 std::string PDUParser::BCDToString(std::string& data) {
273 std::string dst;
274 if (data.empty()) {
275 return "";
276 }
277 int length = data.size();
278 if (length & 0x01) { /* Must be even */
279 return "";
280 }
281 for (int i = 0; i < length; i += 2) {
282 dst += data[i + 1];
283 dst += data[i];
284 }
285
286 if (dst[length -1] == 'F') {
287 dst.replace(length -1, length, "\0");
288 }
289 return dst;
290 }
291
292 // This function is a reverse of the function PDUParser::BCDToString
StringToBCD(std::string_view data)293 std::string PDUParser::StringToBCD(std::string_view data) {
294 std::string dst;
295 if (data.empty()) {
296 return "";
297 }
298 int length = data.size();
299 for (int i = 0; i < length; i += 2) {
300 if (i + 1 < length) {
301 dst += data[i + 1];
302 } else {
303 dst += 'F';
304 }
305 dst += data[i];
306 }
307 return dst;
308 }
309
GetCurrentTimeStamp()310 std::string PDUParser::GetCurrentTimeStamp() {
311 std::string time_stamp;
312 auto now = std::time(0);
313
314 auto local_time = *std::localtime(&now);
315 auto gm_time = *std::gmtime(&now);
316
317 auto t_local_time = std::mktime(&local_time);
318 auto t_gm_time = std::mktime(&gm_time);
319
320 auto tzdiff = (int)std::difftime(t_local_time, t_gm_time) / (60 * 60);
321
322 time_stamp += IntToHexString(local_time.tm_year % 100);
323 time_stamp += IntToHexString(local_time.tm_mon + 1);
324 time_stamp += IntToHexString(local_time.tm_mday);
325 time_stamp += IntToHexString(local_time.tm_hour);
326 time_stamp += IntToHexString(local_time.tm_min);
327 time_stamp += IntToHexString(local_time.tm_sec);
328 time_stamp += IntToHexStringTimeZoneDiff(tzdiff);
329
330 return time_stamp;
331 }
332
333 } // namespace cuttlefish
334