1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is "SMS Library for the Java platform".
15 *
16 * The Initial Developer of the Original Code is Markus Eriksson.
17 * Portions created by the Initial Developer are Copyright (C) 2002
18 * the Initial Developer. All Rights Reserved.
19 *
20 * Contributor(s):
21 *
22 * Alternatively, the contents of this file may be used under the terms of
23 * either the GNU General Public License Version 2 or later (the "GPL"), or
24 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 * in which case the provisions of the GPL or the LGPL are applicable instead
26 * of those above. If you wish to allow use of your version of this file only
27 * under the terms of either the GPL or the LGPL, and not to allow others to
28 * use your version of this file under the terms of the MPL, indicate your
29 * decision by deleting the provisions above and replace them with the notice
30 * and other provisions required by the GPL or the LGPL. If you do not delete
31 * the provisions above, a recipient may use your version of this file under
32 * the terms of any one of the MPL, the GPL or the LGPL.
33 *
34 * ***** END LICENSE BLOCK ***** */
35 package org.marre.sms.transport.gsm;
36
37 import java.io.ByteArrayOutputStream;
38 import java.io.IOException;
39 import java.io.OutputStream;
40
41 import org.marre.sms.SmsAddress;
42 import org.marre.sms.SmsDcs;
43 import org.marre.sms.SmsException;
44 import org.marre.sms.SmsPdu;
45 import org.marre.sms.SmsPduUtil;
46 import org.marre.sms.SmsUserData;
47
48 /***
49 * Builds GSM pdu encoded messages.
50 *
51 * @todo Add support for validity period.
52 *
53 * @author Markus Eriksson
54 * @version $Id: GsmEncoder.java,v 1.7 2005/11/26 16:39:33 c95men Exp $
55 */
56 public final class GsmEncoder
57 {
58 private GsmEncoder()
59 {
60 // Utility class
61 }
62
63 /***
64 * Encodes the given sms pdu into a gsm sms pdu.
65 *
66 * @param thePdu
67 * @param theDestination
68 * @param theSender
69 * @return
70 * @throws SmsException
71 */
72 public static byte[] encodePdu(SmsPdu thePdu, SmsAddress theDestination, SmsAddress theSender)
73 throws SmsException
74 {
75 if (thePdu.getDcs().getAlphabet() == SmsDcs.ALPHABET_GSM)
76 {
77 return encodeSeptetPdu(thePdu, theDestination, theSender);
78 }
79 else
80 {
81 return encodeOctetPdu(thePdu, theDestination, theSender);
82 }
83 }
84
85 /***
86 * Encodes an septet encoded pdu.
87 *
88 * @param thePdu
89 * @param theDestination
90 * @param theSender
91 * @return
92 * @throws SmsException
93 */
94 private static byte[] encodeSeptetPdu(SmsPdu thePdu, SmsAddress theDestination, SmsAddress theSender)
95 throws SmsException
96 {
97 SmsUserData userData = thePdu.getUserData();
98 byte[] ud = userData.getData();
99 byte[] udh = thePdu.getUserDataHeaders();
100
101 int nUdSeptets = userData.getLength();
102 int nUdBits = 0;
103
104 int nUdhBytes = (udh == null) ? 0 : udh.length;
105
106 // UDH + UDHL
107 int nUdhBits = 0;
108
109 // UD + UDH + UDHL
110 int nTotalBytes = 0;
111 int nTotalBits = 0;
112 int nTotalSeptets = 0;
113
114 int nFillBits = 0;
115
116 ByteArrayOutputStream baos = new ByteArrayOutputStream(161);
117
118 try
119 {
120 // UDH?
121 if (nUdhBytes == 0)
122 {
123 // TP-Message-Type-Indicator = SUBMIT
124 // TP-Reject-Duplicates = ON
125 // TP-Validity-Period-Format = No field
126 // TP-Status-Report-Request = No
127 // TP-User-Data-Header = No
128 // TP-Reply-Path = No
129 baos.write(0x01);
130 }
131 else
132 {
133 // +1 is for the UDHL
134 nUdhBits = nUdhBytes * 8;
135
136 if ( (nUdhBits % 7) > 0 )
137 {
138 nFillBits = 7 - (nUdhBits % 7);
139 }
140
141 // TP-Message-Type-Indicator = SUBMIT
142 // TP-Reject-Duplicates = ON
143 // TP-Validity-Period-Format = No field
144 // TP-Status-Report-Request = No
145 // TP-User-Data-Header = Yes
146 // TP-Reply-Path = No
147 baos.write(0x41);
148 }
149
150 nUdBits = nUdSeptets * 7;
151
152 nTotalBits = nUdBits + nFillBits + nUdhBits;
153 nTotalSeptets = nTotalBits / 7;
154
155 nTotalBytes = nTotalBits / 8;
156 if (nTotalBits % 8 > 0)
157 {
158 nTotalBytes += 1;
159 }
160
161 // TP-Message-Reference
162 // Leave to 0x00, MS will set it
163 baos.write(0x00);
164
165 // 2-12 octets
166 // TP-DA
167 // - 1:st octet - length of address (4 bits)
168 // - 2:nd octet
169 // - myBit 7 - always 1
170 // - myBit 4-6 - TON
171 // - myBit 0-3 - NPI
172 // - n octets - BCD
173 writeDestinationAddress(baos, theDestination);
174
175 // TP-PID
176 baos.write(0x00);
177
178 // TP-DCS
179 // UCS, septets, language, SMS class...
180 baos.write(thePdu.getDcs().getValue());
181
182 // TP-VP - Optional
183 // Probably not needed
184
185 // UDH?
186 if ((udh == null) || (udh.length == 0))
187 {
188 // TP-UDL
189 baos.write(nUdSeptets);
190
191 // TP-UD
192 baos.write(ud);
193 }
194 else
195 {
196 // The whole UD PDU
197 byte[] fullUd = new byte[nTotalBytes];
198
199 // TP-UDL
200 // UDL includes the length of the UDHL
201 baos.write(nTotalSeptets);
202
203 // TP-UDH (including user data header length)
204 System.arraycopy(udh, 0, fullUd, 0, nUdhBytes);
205
206 // TP-UD
207 SmsPduUtil.arrayCopy(ud, 0, fullUd, nUdhBytes, nFillBits, nUdBits);
208
209 baos.write(fullUd);
210 }
211 baos.close();
212 }
213 catch (IOException ex)
214 {
215 throw new SmsException(ex);
216 }
217
218 return baos.toByteArray();
219 }
220
221 /***
222 * Encodes an octet encoded sms pdu.
223 *
224 * @param thePdu
225 * @param theDestination
226 * @param theSender
227 * @return
228 * @throws SmsException
229 */
230 private static byte[] encodeOctetPdu(SmsPdu thePdu, SmsAddress theDestination, SmsAddress theSender)
231 throws SmsException
232 {
233 SmsUserData userData = thePdu.getUserData();
234 byte[] ud = userData.getData();
235 byte[] udh = thePdu.getUserDataHeaders();
236
237 ByteArrayOutputStream baos = new ByteArrayOutputStream(200);
238
239 try
240 {
241 int nUdBytes = userData.getLength();
242 int nUdhBytes = (udh == null) ? 0 : udh.length;
243 // +1 For the UDH Length
244 int tpUdl = nUdBytes + nUdhBytes + 1;
245
246 // UDH?
247 if (nUdhBytes == 0)
248 {
249 // TP-Message-Type-Indicator = SUBMIT
250 // TP-Reject-Duplicates = ON
251 // TP-Validity-Period-Format = No field
252 // TP-Status-Report-Request = No
253 // TP-User-Data-Header = No
254 // TP-Reply-Path = No
255 baos.write(0x01);
256 }
257 else
258 {
259 // TP-Message-Type-Indicator = SUBMIT
260 // TP-Reject-Duplicates = ON
261 // TP-Validity-Period-Format = No field
262 // TP-Status-Report-Request = No
263 // TP-User-Data-Header = Yes
264 // TP-Reply-Path = No
265 baos.write(0x41);
266 }
267
268 // TP-Message-Reference
269 // Leave to 0x00, MS will set it
270 baos.write(0x00);
271
272 // 2-12 octets
273 // TP-DA
274 // - 1:st octet - length of address (4 bits)
275 // - 2:nd octet
276 // - myBit 7 - always 1
277 // - myBit 4-6 - TON
278 // - myBit 0-3 - NPI
279 // - n octets - BCD
280 writeDestinationAddress(baos, theDestination);
281
282 // TP-PID
283 baos.write(0x00);
284
285 // TP-DCS
286 baos.write(thePdu.getDcs().getValue());
287
288 // 1 octet/ 7 octets
289 // TP-VP - Optional
290
291 // UDH?
292 if (nUdhBytes == 0)
293 {
294 // 1 Integer
295 // TP-UDL
296 // UDL includes the length of UDH
297 baos.write(nUdBytes);
298
299 // n octets
300 // TP-UD
301 baos.write(ud);
302 }
303 else
304 {
305 // The whole UD PDU without the header length byte
306 byte[] fullUd = new byte[nUdBytes + nUdhBytes];
307
308 // TP-UDL includes the length of UDH
309 // +1 is for the size header...
310 baos.write(nUdBytes + nUdhBytes);
311
312 // TP-UDH (including user data header length)
313 System.arraycopy(udh, 0, fullUd, 0, nUdhBytes);
314 // TP-UD
315 System.arraycopy(ud, 0, fullUd, nUdhBytes, nUdBytes);
316
317 baos.write(fullUd);
318 }
319 baos.close();
320 }
321 catch (IOException ex)
322 {
323 throw new SmsException(ex);
324 }
325
326 return baos.toByteArray();
327 }
328
329 /***
330 * Writes a destination address to the given stream in the correct format
331 *
332 * @param theOs Stream to write to
333 * @param theDestination Destination address to encode
334 * @throws IOException Thrown if failing to write to the stream
335 */
336 private static void writeDestinationAddress(OutputStream theOs, SmsAddress theDestination)
337 throws IOException
338 {
339 String address = theDestination.getAddress();
340 int ton = theDestination.getTypeOfNumber();
341 int npi = theDestination.getNumberingPlanIdentification();
342
343 // trim leading + from address
344 if (address.charAt(0) == '+')
345 {
346 address = address.substring(1);
347 }
348
349 // Length in semi octets
350 theOs.write(address.length());
351
352 // Type Of Address
353 theOs.write(0x80 | ton << 4 | npi);
354
355 // BCD encode
356 SmsPduUtil.writeBcdNumber(theOs, address);
357 }
358 }