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