View Javadoc

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 }