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