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