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