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