• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 
17 package com.android.cellbroadcastservice;
18 
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Objects;
24 
25 /**
26  * SMS user data header, as specified in TS 23.040 9.2.3.24.
27  */
28 public class SmsHeader {
29 
30     // TODO(cleanup): this data structure is generally referred to as
31     // the 'user data header' or UDH, and so the class name should
32     // change to reflect this...
33 
34     /**
35      * SMS user data header information element identifiers.
36      * (see TS 23.040 9.2.3.24)
37      */
38     public static final int ELT_ID_CONCATENATED_8_BIT_REFERENCE = 0x00;
39     public static final int ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION = 0x01;
40     public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT = 0x04;
41     public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT = 0x05;
42     public static final int ELT_ID_SMSC_CONTROL_PARAMS = 0x06;
43     public static final int ELT_ID_UDH_SOURCE_INDICATION = 0x07;
44     public static final int ELT_ID_CONCATENATED_16_BIT_REFERENCE = 0x08;
45     public static final int ELT_ID_WIRELESS_CTRL_MSG_PROTOCOL = 0x09;
46     public static final int ELT_ID_TEXT_FORMATTING = 0x0A;
47     public static final int ELT_ID_PREDEFINED_SOUND = 0x0B;
48     public static final int ELT_ID_USER_DEFINED_SOUND = 0x0C;
49     public static final int ELT_ID_PREDEFINED_ANIMATION = 0x0D;
50     public static final int ELT_ID_LARGE_ANIMATION = 0x0E;
51     public static final int ELT_ID_SMALL_ANIMATION = 0x0F;
52     public static final int ELT_ID_LARGE_PICTURE = 0x10;
53     public static final int ELT_ID_SMALL_PICTURE = 0x11;
54     public static final int ELT_ID_VARIABLE_PICTURE = 0x12;
55     public static final int ELT_ID_USER_PROMPT_INDICATOR = 0x13;
56     public static final int ELT_ID_EXTENDED_OBJECT = 0x14;
57     public static final int ELT_ID_REUSED_EXTENDED_OBJECT = 0x15;
58     public static final int ELT_ID_COMPRESSION_CONTROL = 0x16;
59     public static final int ELT_ID_OBJECT_DISTR_INDICATOR = 0x17;
60     public static final int ELT_ID_STANDARD_WVG_OBJECT = 0x18;
61     public static final int ELT_ID_CHARACTER_SIZE_WVG_OBJECT = 0x19;
62     public static final int ELT_ID_EXTENDED_OBJECT_DATA_REQUEST_CMD = 0x1A;
63     public static final int ELT_ID_RFC_822_EMAIL_HEADER = 0x20;
64     public static final int ELT_ID_HYPERLINK_FORMAT_ELEMENT = 0x21;
65     public static final int ELT_ID_REPLY_ADDRESS_ELEMENT = 0x22;
66     public static final int ELT_ID_ENHANCED_VOICE_MAIL_INFORMATION = 0x23;
67     public static final int ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT = 0x24;
68     public static final int ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT = 0x25;
69 
70     public static final int PORT_WAP_PUSH = 2948;
71     public static final int PORT_WAP_WSP = 9200;
72 
73     /** The maximum number of payload bytes per message */
74     public static final int MAX_USER_DATA_BYTES = 140;
75 
76     @Override
equals(Object o)77     public boolean equals(Object o) {
78         if (this == o) return true;
79         if (o == null || getClass() != o.getClass()) return false;
80         SmsHeader smsHeader = (SmsHeader) o;
81         return languageTable == smsHeader.languageTable
82                 && languageShiftTable == smsHeader.languageShiftTable
83                 && Objects.equals(portAddrs, smsHeader.portAddrs)
84                 && Objects.equals(concatRef, smsHeader.concatRef)
85                 && Objects.equals(specialSmsMsgList, smsHeader.specialSmsMsgList)
86                 && Objects.equals(miscEltList, smsHeader.miscEltList);
87     }
88 
89     @Override
hashCode()90     public int hashCode() {
91         return Objects.hash(portAddrs, concatRef, specialSmsMsgList, miscEltList, languageTable,
92                 languageShiftTable);
93     }
94 
95     /**
96      * Port addresses used in creating and parsing SmsHeader.
97      */
98     public static class PortAddrs {
PortAddrs()99         public PortAddrs() {
100         }
101 
102         public int destPort;
103         public int origPort;
104         public boolean areEightBits;
105 
106         @Override
equals(Object o)107         public boolean equals(Object o) {
108             if (this == o) return true;
109             if (o == null || getClass() != o.getClass()) return false;
110             PortAddrs portAddrs = (PortAddrs) o;
111             return destPort == portAddrs.destPort
112                     && origPort == portAddrs.origPort
113                     && areEightBits == portAddrs.areEightBits;
114         }
115 
116         @Override
hashCode()117         public int hashCode() {
118             return Objects.hash(destPort, origPort, areEightBits);
119         }
120     }
121 
122     /**
123      * Concatenated reference used in creating and parsing SmsHeader.
124      */
125     public static class ConcatRef {
ConcatRef()126         public ConcatRef() {
127         }
128 
129         public int refNumber;
130         public int seqNumber;
131         public int msgCount;
132         public boolean isEightBits;
133 
134         @Override
equals(Object o)135         public boolean equals(Object o) {
136             if (this == o) return true;
137             if (o == null || getClass() != o.getClass()) return false;
138             ConcatRef concatRef = (ConcatRef) o;
139             return refNumber == concatRef.refNumber
140                     && seqNumber == concatRef.seqNumber
141                     && msgCount == concatRef.msgCount
142                     && isEightBits == concatRef.isEightBits;
143         }
144 
145         @Override
hashCode()146         public int hashCode() {
147             return Objects.hash(refNumber, seqNumber, msgCount, isEightBits);
148         }
149     }
150 
151     /**
152      * Special SMS message indicator, used in creating and parsing SmsHeader.
153      */
154     public static class SpecialSmsMsg {
155         public int msgIndType;
156         public int msgCount;
157 
158         @Override
equals(Object o)159         public boolean equals(Object o) {
160             if (this == o) return true;
161             if (o == null || getClass() != o.getClass()) return false;
162             SpecialSmsMsg that = (SpecialSmsMsg) o;
163             return msgIndType == that.msgIndType
164                     && msgCount == that.msgCount;
165         }
166 
167         @Override
hashCode()168         public int hashCode() {
169             return Objects.hash(msgIndType, msgCount);
170         }
171     }
172 
173     /**
174      * A header element that is not explicitly parsed, meaning not
175      * PortAddrs or ConcatRef or SpecialSmsMsg.
176      */
177     public static class MiscElt {
178         public int id;
179         public byte[] data;
180 
181         @Override
equals(Object o)182         public boolean equals(Object o) {
183             if (this == o) return true;
184             if (o == null || getClass() != o.getClass()) return false;
185             MiscElt miscElt = (MiscElt) o;
186             return id == miscElt.id
187                     && Arrays.equals(data, miscElt.data);
188         }
189 
190         @Override
hashCode()191         public int hashCode() {
192             int result = Objects.hash(id);
193             result = 31 * result + Arrays.hashCode(data);
194             return result;
195         }
196     }
197 
198     public PortAddrs portAddrs;
199     public ConcatRef concatRef;
200     public ArrayList<SpecialSmsMsg> specialSmsMsgList = new ArrayList<SpecialSmsMsg>();
201     public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>();
202 
203     /** 7 bit national language locking shift table, or 0 for GSM default 7 bit alphabet. */
204     public int languageTable;
205 
206     /** 7 bit national language single shift table, or 0 for GSM default 7 bit extension table. */
207     public int languageShiftTable;
208 
SmsHeader()209     public SmsHeader() {
210     }
211 
212     /**
213      * Create structured SmsHeader object from serialized byte array representation.
214      * (see TS 23.040 9.2.3.24)
215      *
216      * @param data is user data header bytes
217      * @return SmsHeader object
218      */
fromByteArray(byte[] data)219     public static SmsHeader fromByteArray(byte[] data) {
220         ByteArrayInputStream inStream = new ByteArrayInputStream(data);
221         SmsHeader smsHeader = new SmsHeader();
222         while (inStream.available() > 0) {
223             /**
224              * NOTE: as defined in the spec, ConcatRef and PortAddr
225              * fields should not reoccur, but if they do the last
226              * occurrence is to be used.  Also, for ConcatRef
227              * elements, if the count is zero, sequence is zero, or
228              * sequence is larger than count, the entire element is to
229              * be ignored.
230              */
231             int id = inStream.read();
232             int length = inStream.read();
233             ConcatRef concatRef;
234             PortAddrs portAddrs;
235             switch (id) {
236                 case ELT_ID_CONCATENATED_8_BIT_REFERENCE:
237                     concatRef = new ConcatRef();
238                     concatRef.refNumber = inStream.read();
239                     concatRef.msgCount = inStream.read();
240                     concatRef.seqNumber = inStream.read();
241                     concatRef.isEightBits = true;
242                     if (concatRef.msgCount != 0 && concatRef.seqNumber != 0
243                             && concatRef.seqNumber <= concatRef.msgCount) {
244                         smsHeader.concatRef = concatRef;
245                     }
246                     break;
247                 case ELT_ID_CONCATENATED_16_BIT_REFERENCE:
248                     concatRef = new ConcatRef();
249                     concatRef.refNumber = (inStream.read() << 8) | inStream.read();
250                     concatRef.msgCount = inStream.read();
251                     concatRef.seqNumber = inStream.read();
252                     concatRef.isEightBits = false;
253                     if (concatRef.msgCount != 0 && concatRef.seqNumber != 0
254                             && concatRef.seqNumber <= concatRef.msgCount) {
255                         smsHeader.concatRef = concatRef;
256                     }
257                     break;
258                 case ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT:
259                     portAddrs = new PortAddrs();
260                     portAddrs.destPort = inStream.read();
261                     portAddrs.origPort = inStream.read();
262                     portAddrs.areEightBits = true;
263                     smsHeader.portAddrs = portAddrs;
264                     break;
265                 case ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT:
266                     portAddrs = new PortAddrs();
267                     portAddrs.destPort = (inStream.read() << 8) | inStream.read();
268                     portAddrs.origPort = (inStream.read() << 8) | inStream.read();
269                     portAddrs.areEightBits = false;
270                     smsHeader.portAddrs = portAddrs;
271                     break;
272                 case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT:
273                     smsHeader.languageShiftTable = inStream.read();
274                     break;
275                 case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:
276                     smsHeader.languageTable = inStream.read();
277                     break;
278                 case ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION:
279                     SpecialSmsMsg specialSmsMsg = new SpecialSmsMsg();
280                     specialSmsMsg.msgIndType = inStream.read();
281                     specialSmsMsg.msgCount = inStream.read();
282                     smsHeader.specialSmsMsgList.add(specialSmsMsg);
283                     break;
284                 default:
285                     MiscElt miscElt = new MiscElt();
286                     miscElt.id = id;
287                     miscElt.data = new byte[length];
288                     inStream.read(miscElt.data, 0, length);
289                     smsHeader.miscEltList.add(miscElt);
290             }
291         }
292         return smsHeader;
293     }
294 
295     /**
296      * Create serialized byte array representation from structured SmsHeader object.
297      * (see TS 23.040 9.2.3.24)
298      *
299      * @return Byte array representing the SmsHeader
300      */
toByteArray(SmsHeader smsHeader)301     public static byte[] toByteArray(SmsHeader smsHeader) {
302         if ((smsHeader.portAddrs == null) && (smsHeader.concatRef == null)
303                 && (smsHeader.specialSmsMsgList.isEmpty()) && (smsHeader.miscEltList.isEmpty()) && (
304                 smsHeader.languageShiftTable == 0) && (smsHeader.languageTable == 0)) {
305             return null;
306         }
307 
308         ByteArrayOutputStream outStream =
309                 new ByteArrayOutputStream(MAX_USER_DATA_BYTES);
310         ConcatRef concatRef = smsHeader.concatRef;
311         if (concatRef != null) {
312             if (concatRef.isEightBits) {
313                 outStream.write(ELT_ID_CONCATENATED_8_BIT_REFERENCE);
314                 outStream.write(3);
315                 outStream.write(concatRef.refNumber);
316             } else {
317                 outStream.write(ELT_ID_CONCATENATED_16_BIT_REFERENCE);
318                 outStream.write(4);
319                 outStream.write(concatRef.refNumber >>> 8);
320                 outStream.write(concatRef.refNumber & 0x00FF);
321             }
322             outStream.write(concatRef.msgCount);
323             outStream.write(concatRef.seqNumber);
324         }
325         PortAddrs portAddrs = smsHeader.portAddrs;
326         if (portAddrs != null) {
327             if (portAddrs.areEightBits) {
328                 outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT);
329                 outStream.write(2);
330                 outStream.write(portAddrs.destPort);
331                 outStream.write(portAddrs.origPort);
332             } else {
333                 outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT);
334                 outStream.write(4);
335                 outStream.write(portAddrs.destPort >>> 8);
336                 outStream.write(portAddrs.destPort & 0x00FF);
337                 outStream.write(portAddrs.origPort >>> 8);
338                 outStream.write(portAddrs.origPort & 0x00FF);
339             }
340         }
341         if (smsHeader.languageShiftTable != 0) {
342             outStream.write(ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT);
343             outStream.write(1);
344             outStream.write(smsHeader.languageShiftTable);
345         }
346         if (smsHeader.languageTable != 0) {
347             outStream.write(ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT);
348             outStream.write(1);
349             outStream.write(smsHeader.languageTable);
350         }
351         for (SpecialSmsMsg specialSmsMsg : smsHeader.specialSmsMsgList) {
352             outStream.write(ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION);
353             outStream.write(2);
354             outStream.write(specialSmsMsg.msgIndType & 0xFF);
355             outStream.write(specialSmsMsg.msgCount & 0xFF);
356         }
357         for (MiscElt miscElt : smsHeader.miscEltList) {
358             outStream.write(miscElt.id);
359             outStream.write(miscElt.data.length);
360             outStream.write(miscElt.data, 0, miscElt.data.length);
361         }
362         return outStream.toByteArray();
363     }
364 }
365