1 /*
2  * Copyright (C) 2008 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 java.util.HashMap;
20 
21 /**
22  * Implement the WSP data type decoder.
23  *
24  * @hide
25  */
26 public class WspTypeDecoder {
27 
28     private static final int WAP_PDU_SHORT_LENGTH_MAX = 30;
29     private static final int WAP_PDU_LENGTH_QUOTE = 31;
30 
31     public static final int PDU_TYPE_PUSH = 0x06;
32     public static final int PDU_TYPE_CONFIRMED_PUSH = 0x07;
33 
34     private final static HashMap<Integer, String> WELL_KNOWN_MIME_TYPES =
35             new HashMap<Integer, String>();
36 
37     private final static HashMap<Integer, String> WELL_KNOWN_PARAMETERS =
38             new HashMap<Integer, String>();
39 
40     public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f;
41     private static final int Q_VALUE = 0x00;
42 
43     static {
44         WELL_KNOWN_MIME_TYPES.put(0x00, "*/*");
45         WELL_KNOWN_MIME_TYPES.put(0x01, "text/*");
46         WELL_KNOWN_MIME_TYPES.put(0x02, "text/html");
47         WELL_KNOWN_MIME_TYPES.put(0x03, "text/plain");
48         WELL_KNOWN_MIME_TYPES.put(0x04, "text/x-hdml");
49         WELL_KNOWN_MIME_TYPES.put(0x05, "text/x-ttml");
50         WELL_KNOWN_MIME_TYPES.put(0x06, "text/x-vCalendar");
51         WELL_KNOWN_MIME_TYPES.put(0x07, "text/x-vCard");
52         WELL_KNOWN_MIME_TYPES.put(0x08, "text/vnd.wap.wml");
53         WELL_KNOWN_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript");
54         WELL_KNOWN_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event");
55         WELL_KNOWN_MIME_TYPES.put(0x0B, "multipart/*");
56         WELL_KNOWN_MIME_TYPES.put(0x0C, "multipart/mixed");
57         WELL_KNOWN_MIME_TYPES.put(0x0D, "multipart/form-data");
58         WELL_KNOWN_MIME_TYPES.put(0x0E, "multipart/byterantes");
59         WELL_KNOWN_MIME_TYPES.put(0x0F, "multipart/alternative");
60         WELL_KNOWN_MIME_TYPES.put(0x10, "application/*");
61         WELL_KNOWN_MIME_TYPES.put(0x11, "application/java-vm");
62         WELL_KNOWN_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded");
63         WELL_KNOWN_MIME_TYPES.put(0x13, "application/x-hdmlc");
64         WELL_KNOWN_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc");
65         WELL_KNOWN_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc");
66         WELL_KNOWN_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc");
67         WELL_KNOWN_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof");
68         WELL_KNOWN_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate");
69         WELL_KNOWN_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate");
70         WELL_KNOWN_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert");
71         WELL_KNOWN_MIME_TYPES.put(0x1B, "application/x-x509-user-cert");
72         WELL_KNOWN_MIME_TYPES.put(0x1C, "image/*");
73         WELL_KNOWN_MIME_TYPES.put(0x1D, "image/gif");
74         WELL_KNOWN_MIME_TYPES.put(0x1E, "image/jpeg");
75         WELL_KNOWN_MIME_TYPES.put(0x1F, "image/tiff");
76         WELL_KNOWN_MIME_TYPES.put(0x20, "image/png");
77         WELL_KNOWN_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp");
78         WELL_KNOWN_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*");
79         WELL_KNOWN_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed");
80         WELL_KNOWN_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data");
81         WELL_KNOWN_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges");
82         WELL_KNOWN_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative");
83         WELL_KNOWN_MIME_TYPES.put(0x27, "application/xml");
84         WELL_KNOWN_MIME_TYPES.put(0x28, "text/xml");
85         WELL_KNOWN_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml");
86         WELL_KNOWN_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert");
87         WELL_KNOWN_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert");
88         WELL_KNOWN_MIME_TYPES.put(0x2C, "application/x-x968-user-cert");
89         WELL_KNOWN_MIME_TYPES.put(0x2D, "text/vnd.wap.si");
90         WELL_KNOWN_MIME_TYPES.put(0x2E, "application/vnd.wap.sic");
91         WELL_KNOWN_MIME_TYPES.put(0x2F, "text/vnd.wap.sl");
92         WELL_KNOWN_MIME_TYPES.put(0x30, "application/vnd.wap.slc");
93         WELL_KNOWN_MIME_TYPES.put(0x31, "text/vnd.wap.co");
94         WELL_KNOWN_MIME_TYPES.put(0x32, "application/vnd.wap.coc");
95         WELL_KNOWN_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related");
96         WELL_KNOWN_MIME_TYPES.put(0x34, "application/vnd.wap.sia");
97         WELL_KNOWN_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml");
98         WELL_KNOWN_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml");
99         WELL_KNOWN_MIME_TYPES.put(0x37, "application/pkcs7-mime");
100         WELL_KNOWN_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate");
101         WELL_KNOWN_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate");
102         WELL_KNOWN_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response");
103         WELL_KNOWN_MIME_TYPES.put(0x3B, "application/xhtml+xml");
104         WELL_KNOWN_MIME_TYPES.put(0x3C, "application/wml+xml");
105         WELL_KNOWN_MIME_TYPES.put(0x3D, "text/css");
106         WELL_KNOWN_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message");
107         WELL_KNOWN_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate");
108         WELL_KNOWN_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml");
109         WELL_KNOWN_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml");
110         WELL_KNOWN_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml");
111         WELL_KNOWN_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml");
112         WELL_KNOWN_MIME_TYPES.put(0x44, "application/vnd.syncml.notification");
113         WELL_KNOWN_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml");
114         WELL_KNOWN_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir");
115         WELL_KNOWN_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml");
116         WELL_KNOWN_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message");
117         WELL_KNOWN_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content");
118         WELL_KNOWN_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml");
119         WELL_KNOWN_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml");
120         WELL_KNOWN_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml");
121         WELL_KNOWN_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml");
122         WELL_KNOWN_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification");
123         WELL_KNOWN_MIME_TYPES.put(0x4F, "audio/*");
124         WELL_KNOWN_MIME_TYPES.put(0x50, "video/*");
125         WELL_KNOWN_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml");
126         WELL_KNOWN_MIME_TYPES.put(0x52, "application/mikey");
127         WELL_KNOWN_MIME_TYPES.put(0x53, "application/vnd.oma.dcd");
128         WELL_KNOWN_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc");
129 
130         WELL_KNOWN_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml");
131         WELL_KNOWN_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal");
132         WELL_KNOWN_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml");
133         WELL_KNOWN_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml");
134         WELL_KNOWN_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml");
135         WELL_KNOWN_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml");
136         WELL_KNOWN_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri");
137         WELL_KNOWN_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set");
138         WELL_KNOWN_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml");
139         WELL_KNOWN_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml");
140         WELL_KNOWN_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml");
141         WELL_KNOWN_MIME_TYPES.put(0x020C, "image/x-up-wpng");
142         WELL_KNOWN_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml");
143         WELL_KNOWN_MIME_TYPES.put(0x0301, "application/iota.mmc-xml");
144         WELL_KNOWN_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml");
145         WELL_KNOWN_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml");
146         WELL_KNOWN_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml");
147         WELL_KNOWN_MIME_TYPES.put(0x0305, "text/calendar");
148         WELL_KNOWN_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml");
149         WELL_KNOWN_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml");
150         WELL_KNOWN_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml");
151         WELL_KNOWN_MIME_TYPES.put(0x0309, "text/directory;profile=vCard");
152         WELL_KNOWN_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml");
153         WELL_KNOWN_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response");
154         WELL_KNOWN_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml");
155         WELL_KNOWN_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip");
156         WELL_KNOWN_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml");
157         WELL_KNOWN_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml");
158         WELL_KNOWN_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf");
159         WELL_KNOWN_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub");
160         WELL_KNOWN_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init");
161         WELL_KNOWN_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml");
162         WELL_KNOWN_MIME_TYPES.put(0x0314, "application/oma-directory+xml");
163         WELL_KNOWN_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2");
164         WELL_KNOWN_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml");
165         WELL_KNOWN_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2");
166         WELL_KNOWN_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap");
167         WELL_KNOWN_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml");
168         WELL_KNOWN_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid");
169         WELL_KNOWN_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger");
170 
171         WELL_KNOWN_PARAMETERS.put(0x00, "Q");
172         WELL_KNOWN_PARAMETERS.put(0x01, "Charset");
173         WELL_KNOWN_PARAMETERS.put(0x02, "Level");
174         WELL_KNOWN_PARAMETERS.put(0x03, "Type");
175         WELL_KNOWN_PARAMETERS.put(0x07, "Differences");
176         WELL_KNOWN_PARAMETERS.put(0x08, "Padding");
177         WELL_KNOWN_PARAMETERS.put(0x09, "Type");
178         WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age");
179         WELL_KNOWN_PARAMETERS.put(0x10, "Secure");
180         WELL_KNOWN_PARAMETERS.put(0x11, "SEC");
181         WELL_KNOWN_PARAMETERS.put(0x12, "MAC");
182         WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date");
183         WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date");
184         WELL_KNOWN_PARAMETERS.put(0x15, "Read-date");
185         WELL_KNOWN_PARAMETERS.put(0x16, "Size");
186         WELL_KNOWN_PARAMETERS.put(0x17, "Name");
187         WELL_KNOWN_PARAMETERS.put(0x18, "Filename");
188         WELL_KNOWN_PARAMETERS.put(0x19, "Start");
189         WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info");
190         WELL_KNOWN_PARAMETERS.put(0x1B, "Comment");
191         WELL_KNOWN_PARAMETERS.put(0x1C, "Domain");
192         WELL_KNOWN_PARAMETERS.put(0x1D, "Path");
193     }
194 
195     public static final String CONTENT_TYPE_B_PUSH_CO = "application/vnd.wap.coc";
196     public static final String CONTENT_TYPE_B_MMS = "application/vnd.wap.mms-message";
197     public static final String CONTENT_TYPE_B_PUSH_SYNCML_NOTI = "application/vnd.syncml.notification";
198 
199     byte[] mWspData;
200     int    mDataLength;
201     long   mUnsigned32bit;
202     String mStringValue;
203 
204     HashMap<String, String> mContentParameters;
205 
WspTypeDecoder(byte[] pdu)206     public WspTypeDecoder(byte[] pdu) {
207         mWspData = pdu;
208     }
209 
210     /**
211      * Decode the "Text-string" type for WSP pdu
212      *
213      * @param startIndex The starting position of the "Text-string" in this pdu
214      *
215      * @return false when error(not a Text-string) occur
216      *         return value can be retrieved by getValueString() method length of data in pdu can be
217      *         retrieved by getDecodedDataLength() method
218      */
decodeTextString(int startIndex)219     public boolean decodeTextString(int startIndex) {
220         int index = startIndex;
221         while (mWspData[index] != 0) {
222             index++;
223         }
224         mDataLength = index - startIndex + 1;
225         if (mWspData[startIndex] == 127) {
226             mStringValue = new String(mWspData, startIndex + 1, mDataLength - 2);
227         } else {
228             mStringValue = new String(mWspData, startIndex, mDataLength - 1);
229         }
230         return true;
231     }
232 
233     /**
234      * Decode the "Token-text" type for WSP pdu
235      *
236      * @param startIndex The starting position of the "Token-text" in this pdu
237      *
238      * @return always true
239      *         return value can be retrieved by getValueString() method
240      *         length of data in pdu can be retrieved by getDecodedDataLength() method
241      */
decodeTokenText(int startIndex)242     public boolean decodeTokenText(int startIndex) {
243         int index = startIndex;
244         while (mWspData[index] != 0) {
245             index++;
246         }
247         mDataLength = index - startIndex + 1;
248         mStringValue = new String(mWspData, startIndex, mDataLength - 1);
249 
250         return true;
251     }
252 
253     /**
254      * Decode the "Short-integer" type for WSP pdu
255      *
256      * @param startIndex The starting position of the "Short-integer" in this pdu
257      *
258      * @return false when error(not a Short-integer) occur
259      *         return value can be retrieved by getValue32() method
260      *         length of data in pdu can be retrieved by getDecodedDataLength() method
261      */
decodeShortInteger(int startIndex)262     public boolean decodeShortInteger(int startIndex) {
263         if ((mWspData[startIndex] & 0x80) == 0) {
264             return false;
265         }
266         mUnsigned32bit = mWspData[startIndex] & 0x7f;
267         mDataLength = 1;
268         return true;
269     }
270 
271     /**
272      * Decode the "Long-integer" type for WSP pdu
273      *
274      * @param startIndex The starting position of the "Long-integer" in this pdu
275      *
276      * @return false when error(not a Long-integer) occur
277      *         return value can be retrieved by getValue32() method
278      *         length of data in pdu can be retrieved by getDecodedDataLength() method
279      */
decodeLongInteger(int startIndex)280     public boolean decodeLongInteger(int startIndex) {
281         int lengthMultiOctet = mWspData[startIndex] & 0xff;
282 
283         if (lengthMultiOctet > WAP_PDU_SHORT_LENGTH_MAX) {
284             return false;
285         }
286         mUnsigned32bit = 0;
287         for (int i = 1; i <= lengthMultiOctet; i++) {
288             mUnsigned32bit = (mUnsigned32bit << 8) | (mWspData[startIndex + i] & 0xff);
289         }
290         mDataLength = 1 + lengthMultiOctet;
291         return true;
292     }
293 
294     /**
295      * Decode the "Integer-Value" type for WSP pdu
296      *
297      * @param startIndex The starting position of the "Integer-Value" in this pdu
298      *
299      * @return false when error(not a Integer-Value) occur
300      *         return value can be retrieved by getValue32() method
301      *         length of data in pdu can be retrieved by getDecodedDataLength() method
302      */
decodeIntegerValue(int startIndex)303     public boolean decodeIntegerValue(int startIndex) {
304         if (decodeShortInteger(startIndex) == true) {
305             return true;
306         }
307         return decodeLongInteger(startIndex);
308     }
309 
310     /**
311      * Decode the "Uintvar-integer" type for WSP pdu
312      *
313      * @param startIndex The starting position of the "Uintvar-integer" in this pdu
314      *
315      * @return false when error(not a Uintvar-integer) occur
316      *         return value can be retrieved by getValue32() method
317      *         length of data in pdu can be retrieved by getDecodedDataLength() method
318      */
decodeUintvarInteger(int startIndex)319     public boolean decodeUintvarInteger(int startIndex) {
320         int index = startIndex;
321 
322         mUnsigned32bit = 0;
323         while ((mWspData[index] & 0x80) != 0) {
324             if ((index - startIndex) >= 4) {
325                 return false;
326             }
327             mUnsigned32bit = (mUnsigned32bit << 7) | (mWspData[index] & 0x7f);
328             index++;
329         }
330         mUnsigned32bit = (mUnsigned32bit << 7) | (mWspData[index] & 0x7f);
331         mDataLength = index - startIndex + 1;
332         return true;
333     }
334 
335     /**
336      * Decode the "Value-length" type for WSP pdu
337      *
338      * @param startIndex The starting position of the "Value-length" in this pdu
339      *
340      * @return false when error(not a Value-length) occur
341      *         return value can be retrieved by getValue32() method
342      *         length of data in pdu can be retrieved by getDecodedDataLength() method
343      */
decodeValueLength(int startIndex)344     public boolean decodeValueLength(int startIndex) {
345         if ((mWspData[startIndex] & 0xff) > WAP_PDU_LENGTH_QUOTE) {
346             return false;
347         }
348         if (mWspData[startIndex] < WAP_PDU_LENGTH_QUOTE) {
349             mUnsigned32bit = mWspData[startIndex];
350             mDataLength = 1;
351         } else {
352             decodeUintvarInteger(startIndex + 1);
353             mDataLength++;
354         }
355         return true;
356     }
357 
358     /**
359      * Decode the "Extension-media" type for WSP PDU.
360      *
361      * @param startIndex The starting position of the "Extension-media" in this PDU.
362      *
363      * @return false on error, such as if there is no Extension-media at startIndex.
364      *         Side-effects: updates stringValue (available with
365      *         getValueString()), which will be null on error. The length of the
366      *         data in the PDU is available with getValue32(), 0 on error.
367      */
decodeExtensionMedia(int startIndex)368     public boolean decodeExtensionMedia(int startIndex) {
369         int index = startIndex;
370         mDataLength = 0;
371         mStringValue = null;
372         int length = mWspData.length;
373         boolean rtrn = index < length;
374 
375         while (index < length && mWspData[index] != 0) {
376             index++;
377         }
378 
379         mDataLength = index - startIndex + 1;
380         mStringValue = new String(mWspData, startIndex, mDataLength - 1);
381 
382         return rtrn;
383     }
384 
385     /**
386      * Decode the "Constrained-encoding" type for WSP pdu
387      *
388      * @param startIndex The starting position of the "Constrained-encoding" in this pdu
389      *
390      * @return false when error(not a Constrained-encoding) occur
391      *         return value can be retrieved first by getValueString() and second by getValue32() method
392      *         length of data in pdu can be retrieved by getDecodedDataLength() method
393      */
394     public boolean decodeConstrainedEncoding(int startIndex) {
395         if (decodeShortInteger(startIndex) == true) {
396             mStringValue = null;
397             return true;
398         }
399         return decodeExtensionMedia(startIndex);
400     }
401 
402     /**
403      * Decode the "Content-type" type for WSP pdu
404      *
405      * @param startIndex The starting position of the "Content-type" in this pdu
406      *
407      * @return false when error(not a Content-type) occurs
408      *         If a content type exists in the headers (either as inline string, or as well-known
409      *         value), getValueString() will return it. If a 'well known value' is encountered that
410      *         cannot be mapped to a string mime type, getValueString() will return null, and
411      *         getValue32() will return the unknown content type value.
412      *         length of data in pdu can be retrieved by getDecodedDataLength() method
413      *         Any content type parameters will be accessible via getContentParameters()
414      */
415     public boolean decodeContentType(int startIndex) {
416         int mediaPrefixLength;
417         mContentParameters = new HashMap<String, String>();
418 
419         try {
420             if (decodeValueLength(startIndex) == false) {
421                 boolean found = decodeConstrainedEncoding(startIndex);
422                 if (found) {
423                     expandWellKnownMimeType();
424                 }
425                 return found;
426             }
427             int headersLength = (int) mUnsigned32bit;
428             mediaPrefixLength = getDecodedDataLength();
429             if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) {
430                 mDataLength += mediaPrefixLength;
431                 int readLength = mDataLength;
432                 mStringValue = null;
433                 expandWellKnownMimeType();
434                 long wellKnownValue = mUnsigned32bit;
435                 String mimeType = mStringValue;
436                 if (readContentParameters(startIndex + mDataLength,
437                         (headersLength - (mDataLength - mediaPrefixLength)), 0)) {
438                     mDataLength += readLength;
439                     mUnsigned32bit = wellKnownValue;
440                     mStringValue = mimeType;
441                     return true;
442                 }
443                 return false;
444             }
445             if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) {
446                 mDataLength += mediaPrefixLength;
447                 int readLength = mDataLength;
448                 expandWellKnownMimeType();
449                 long wellKnownValue = mUnsigned32bit;
450                 String mimeType = mStringValue;
451                 if (readContentParameters(startIndex + mDataLength,
452                         (headersLength - (mDataLength - mediaPrefixLength)), 0)) {
453                     mDataLength += readLength;
454                     mUnsigned32bit = wellKnownValue;
455                     mStringValue = mimeType;
456                     return true;
457                 }
458             }
459         } catch (ArrayIndexOutOfBoundsException e) {
460             //something doesn't add up
461             return false;
462         }
463         return false;
464     }
465 
466     private boolean readContentParameters(int startIndex, int leftToRead, int accumulator) {
467 
468         int totalRead = 0;
469 
470         if (leftToRead > 0) {
471             byte nextByte = mWspData[startIndex];
472             String value = null;
473             String param = null;
474             if ((nextByte & 0x80) == 0x00 && nextByte > 31) { // untyped
475                 decodeTokenText(startIndex);
476                 param = mStringValue;
477                 totalRead += mDataLength;
478             } else { // typed
479                 if (decodeIntegerValue(startIndex)) {
480                     totalRead += mDataLength;
481                     int wellKnownParameterValue = (int) mUnsigned32bit;
482                     param = WELL_KNOWN_PARAMETERS.get(wellKnownParameterValue);
483                     if (param == null) {
484                         param = "unassigned/0x" + Long.toHexString(wellKnownParameterValue);
485                     }
486                     // special case for the "Q" parameter, value is a uintvar
487                     if (wellKnownParameterValue == Q_VALUE) {
488                         if (decodeUintvarInteger(startIndex + totalRead)) {
489                             totalRead += mDataLength;
490                             value = String.valueOf(mUnsigned32bit);
491                             mContentParameters.put(param, value);
492                             return readContentParameters(startIndex + totalRead, leftToRead
493                                                             - totalRead, accumulator + totalRead);
494                         } else {
495                             return false;
496                         }
497                     }
498                 } else {
499                     return false;
500                 }
501             }
502 
503             if (decodeNoValue(startIndex + totalRead)) {
504                 totalRead += mDataLength;
505                 value = null;
506             } else if (decodeIntegerValue(startIndex + totalRead)) {
507                 totalRead += mDataLength;
508                 int intValue = (int) mUnsigned32bit;
509                 value = String.valueOf(intValue);
510             } else {
511                 decodeTokenText(startIndex + totalRead);
512                 totalRead += mDataLength;
513                 value = mStringValue;
514                 if (value.startsWith("\"")) {
515                     // quoted string, so remove the quote
516                     value = value.substring(1);
517                 }
518             }
519             mContentParameters.put(param, value);
520             return readContentParameters(startIndex + totalRead, leftToRead - totalRead,
521                                             accumulator + totalRead);
522 
523         } else {
524             mDataLength = accumulator;
525             return true;
526         }
527     }
528 
529     /**
530      * Check if the next byte is No-Value
531      *
532      * @param startIndex The starting position of the "Content length" in this pdu
533      *
534      * @return true if and only if the next byte is 0x00
535      */
decodeNoValue(int startIndex)536     private boolean decodeNoValue(int startIndex) {
537         if (mWspData[startIndex] == 0) {
538             mDataLength = 1;
539             return true;
540         } else {
541             return false;
542         }
543     }
544 
545     /**
546      * Populate stringValue with the mime type corresponding to the value in unsigned32bit
547      *
548      * Sets unsigned32bit to -1 if stringValue is already populated
549      */
expandWellKnownMimeType()550     private void expandWellKnownMimeType() {
551         if (mStringValue == null) {
552             int binaryContentType = (int) mUnsigned32bit;
553             mStringValue = WELL_KNOWN_MIME_TYPES.get(binaryContentType);
554         } else {
555             mUnsigned32bit = -1;
556         }
557     }
558 
559     /**
560      * Decode the "Content length" type for WSP pdu
561      *
562      * @param startIndex The starting position of the "Content length" in this pdu
563      *
564      * @return false when error(not a Content length) occur
565      *         return value can be retrieved by getValue32() method
566      *         length of data in pdu can be retrieved by getDecodedDataLength() method
567      */
decodeContentLength(int startIndex)568     public boolean decodeContentLength(int startIndex) {
569         return decodeIntegerValue(startIndex);
570     }
571 
572     /**
573      * Decode the "Content location" type for WSP pdu
574      *
575      * @param startIndex The starting position of the "Content location" in this pdu
576      *
577      * @return false when error(not a Content location) occur
578      *         return value can be retrieved by getValueString() method
579      *         length of data in pdu can be retrieved by getDecodedDataLength() method
580      */
decodeContentLocation(int startIndex)581     public boolean decodeContentLocation(int startIndex) {
582         return decodeTextString(startIndex);
583     }
584 
585     /**
586      * Decode the "X-Wap-Application-Id" type for WSP pdu
587      *
588      * @param startIndex The starting position of the "X-Wap-Application-Id" in this pdu
589      *
590      * @return false when error(not a X-Wap-Application-Id) occur
591      *         return value can be retrieved first by getValueString() and second by getValue32()
592      *         method
593      *         length of data in pdu can be retrieved by getDecodedDataLength() method
594      */
decodeXWapApplicationId(int startIndex)595     public boolean decodeXWapApplicationId(int startIndex) {
596         if (decodeIntegerValue(startIndex) == true) {
597             mStringValue = null;
598             return true;
599         }
600         return decodeTextString(startIndex);
601     }
602 
603     /**
604      * Seek for the "X-Wap-Application-Id" field for WSP pdu
605      *
606      * @param startIndex The starting position of seek pointer
607      * @param endIndex Valid seek area end point
608      *
609      * @return false when error(not a X-Wap-Application-Id) occur
610      *         return value can be retrieved by getValue32()
611      */
seekXWapApplicationId(int startIndex, int endIndex)612     public boolean seekXWapApplicationId(int startIndex, int endIndex) {
613         int index = startIndex;
614 
615         try {
616             for (index = startIndex; index <= endIndex; ) {
617                 /**
618                  * 8.4.1.1  Field name
619                  * Field name is integer or text.
620                  */
621                 if (decodeIntegerValue(index)) {
622                     int fieldValue = (int) getValue32();
623 
624                     if (fieldValue == PARAMETER_ID_X_WAP_APPLICATION_ID) {
625                         mUnsigned32bit = index + 1;
626                         return true;
627                     }
628                 } else {
629                     if (!decodeTextString(index)) return false;
630                 }
631                 index += getDecodedDataLength();
632                 if (index > endIndex) return false;
633 
634                 /**
635                  * 8.4.1.2 Field values
636                  * Value Interpretation of First Octet
637                  * 0 - 30 This octet is followed by the indicated number (0 - 30)
638                         of data octets
639                  * 31 This octet is followed by a uintvar, which indicates the number
640                  *      of data octets after it
641                  * 32 - 127 The value is a text string, terminated by a zero octet
642                         (NUL character)
643                  * 128 - 255 It is an encoded 7-bit value; this header has no more data
644                  */
645                 byte val = mWspData[index];
646                 if (0 <= val && val <= WAP_PDU_SHORT_LENGTH_MAX) {
647                     index += mWspData[index] + 1;
648                 } else if (val == WAP_PDU_LENGTH_QUOTE) {
649                     if (index + 1 >= endIndex) return false;
650                     index++;
651                     if (!decodeUintvarInteger(index)) return false;
652                     index += getDecodedDataLength();
653                 } else if (WAP_PDU_LENGTH_QUOTE < val && val <= 127) {
654                     if (!decodeTextString(index)) return false;
655                     index += getDecodedDataLength();
656                 } else {
657                     index++;
658                 }
659             }
660         } catch (ArrayIndexOutOfBoundsException e) {
661             //seek application ID failed. WSP header might be corrupted
662             return false;
663         }
664         return false;
665     }
666 
667     /**
668      * Decode the "X-Wap-Content-URI" type for WSP pdu
669      *
670      * @param startIndex The starting position of the "X-Wap-Content-URI" in this pdu
671      *
672      * @return false when error(not a X-Wap-Content-URI) occur
673      *         return value can be retrieved by getValueString() method
674      *         length of data in pdu can be retrieved by getDecodedDataLength() method
675      */
decodeXWapContentURI(int startIndex)676     public boolean decodeXWapContentURI(int startIndex) {
677         return decodeTextString(startIndex);
678     }
679 
680     /**
681      * Decode the "X-Wap-Initiator-URI" type for WSP pdu
682      *
683      * @param startIndex The starting position of the "X-Wap-Initiator-URI" in this pdu
684      *
685      * @return false when error(not a X-Wap-Initiator-URI) occur
686      *         return value can be retrieved by getValueString() method
687      *         length of data in pdu can be retrieved by getDecodedDataLength() method
688      */
decodeXWapInitiatorURI(int startIndex)689     public boolean decodeXWapInitiatorURI(int startIndex) {
690         return decodeTextString(startIndex);
691     }
692 
693     /**
694      * The data length of latest operation.
695      */
getDecodedDataLength()696     public int getDecodedDataLength() {
697         return mDataLength;
698     }
699 
700     /**
701      * The 32-bits result of latest operation.
702      */
getValue32()703     public long getValue32() {
704         return mUnsigned32bit;
705     }
706 
707     /**
708      * The String result of latest operation.
709      */
getValueString()710     public String getValueString() {
711         return mStringValue;
712     }
713 
714     /**
715      * Any parameters encountered as part of a decodeContentType() invocation.
716      *
717      * @return a map of content parameters keyed by their names, or null if
718      *         decodeContentType() has not been called If any unassigned
719      *         well-known parameters are encountered, the key of the map will be
720      *         'unassigned/0x...', where '...' is the hex value of the
721      *         unassigned parameter.  If a parameter has No-Value the value will be null.
722      *
723      */
getContentParameters()724     public HashMap<String, String> getContentParameters() {
725         return mContentParameters;
726     }
727 }
728