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
17package com.android.internal.telephony;
18
19import java.util.HashMap;
20
21/**
22 * Implement the WSP data type decoder.
23 *
24 * @hide
25 */
26public 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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
689    public boolean decodeXWapInitiatorURI(int startIndex) {
690        return decodeTextString(startIndex);
691    }
692
693    /**
694     * The data length of latest operation.
695     */
696    public int getDecodedDataLength() {
697        return mDataLength;
698    }
699
700    /**
701     * The 32-bits result of latest operation.
702     */
703    public long getValue32() {
704        return mUnsigned32bit;
705    }
706
707    /**
708     * The String result of latest operation.
709     */
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     */
724    public HashMap<String, String> getContentParameters() {
725        return mContentParameters;
726    }
727}
728